WebdriverIO: How to read baseURL value from wdio.conf.js. inside step definition file - selenium

I am using WebdriverIO for test automation. In wdio.conf.js file I have configured the 'baseUrl' property.
I want to read the 'baseUrl' property value inside my test .js file. How can I do this?

❒ wdio-v5
Lately, after writing a lot of tests for a project rewrite I've came to believe the best way to store/access global config variables is via the global object.
You can define them inside the wdio.conf.js file's hooks. I defined mine in the before hook:
before: function (capabilities, specs) {
// =================
// Assertion Library
// =================
const chai = require('chai');
global.expect = chai.expect;
global.assert = chai.assert;
global.should = chai.should();
// ======================
// Miscellaneous Packages
// ======================
global.langCode = langCode;
global.countryCode = countryCode;
global.request = require('superagent');
global.allowedStatusCodes = [200, 301],
// ===============
// Custom Commands
// ===============
require('./test/custom_commands/aFancyMethod');
require('./test/custom_commands/anotherOne');
require('./test/custom_commands/andAnotherOne');
},
Then, you can access them directly, anywhere in your test-files, or page-objects. This way, you greatly reduce the test-file's footprint (errr... codeprint) because you can call these directly in your test case:
describe(`Testing a random URL`, () => {
it('Should return a HTTP valid status code', async () => {
// Issue a HTTP request for the given URL:
await request
.head('https://random.org')
.then(res => {
console.info(`\n> Status code found: ${res.status} | MIME type found: '${res.type}'\n`);
foundStatusCode = res.status;
})
.catch(err => {
console.info(`\n> Status code found: ${err.status} | Error response found: '${JSON.stringify(err.response)}'\n`);
foundStatusCode = err.status;
});
// Assert the HTTP Status Code:
assert.include(allowedStatusCodes, foundStatusCode, `!AssertError: Route yields a bad status code! Got: ${foundStatusCode} | Expected: ${allowedStatusCodes}`);
});
As opposed to always doing await browser.options.request.head(..., browser.options.baseUrl, etc.
❒ wdio-v4
All the wdio.conf.js file attributes (basically the config object name-value pairs) are also stored inside the browser.options object.
Thus, a more elegant approach to access your global config values from inside your tests would be as presented below:
> browser.options
{ port: 4444,
protocol: 'http',
waitforTimeout: 10000,
waitforInterval: 500,
coloredLogs: true,
deprecationWarnings: false,
logLevel: 'verbose',
baseUrl: 'http://localhost',
// ... etc ...
}
> browser.options.baseUrl
'http://localhost'
I'll go on a limb here and presume you want to read the baseUrl value from your wdio.config.js file, into your test.js file.
TL;DR: In your test.js file heading, add the following:
var config = require('<pathToWdioConfJS>/wdio.conf.js').config;
You can then access any wdio.config.js value via the config.<configOption>, in your case config.baseUrl.
Lastly, I would greatly recommend you read about exports and module exports.
WebdriverIO is built on NodeJS, so you will shoot yourself in the foot on the long run if you don't know how and when to use exports, module.exports, require, or the difference between them.

Use browser.options.baseUrl . If you use require, you're hard coding from that one file, which is fine, but you cannot do a wdio --baseUrl=http://myTestSite2.net to override the "global" baseUrl. Which you might want to do in multiple deployments in the future.

In wdio.config.js file define the url like this
var baseUrl = 'YOUR URL'
exports.config = {
baseUrl: baseUrl,
}
In Test file use / instead of adding complete url in browser.url('/'), it will use the baseUrl from the wdio.config.js file.
browser.url('/')

BaseUrl is available in the config object browser.config.baseUrl
See https://github.com/webdriverio/webdriverio/blob/a4a5a46f786f8548361d7f86834b38f89dcb1690/packages/webdriverio/webdriverio-core.d.ts#L131

just save all your variable in before: function and can be used anywhere in your test.
like the following example i use retry count wdio config file
before: function (capabilities, specs) {
expect = require('chai').expect;
should = require('chai').should();
assert = require('assert');
retryCount=2;
browser.maximizeWindow();

Related

How to change content of VuePress page via Plugin

I am trying to set up a plugin to change the content of a VuePress markdown file on the dev server and production build. According to documentation, I should be able to use the _content and _strippedContent that is available to me with the extendPageData
The following code is what I have set up in a plugin to do this.
module.exports = (options = {}, context) => ({
extendPageData($page) {
const {
_filePath, // file's absolute path
_computed, // access the client global computed mixins at build time, e.g _computed.$localePath.
_content, // file's raw content string
_strippedContent, // file's content string without frontmatter
key, // page's unique hash key
frontmatter, // page's frontmatter object
regularPath, // current page's default link (follow the file hierarchy)
path, // current page's real link (use regularPath when permalink does not exist)
} = $page
$page._content = "replaced"
$page._strippedContent = "replaced"
}
})
The best I can tell is that this code should work as it updates the $page._content however it is not showing testing but rather the original content.
I know that I am getting into this code as I can console.log from the file and it shows in the console.
I fear that $page._content is immutable and wonder if there is a way to do this kind of content swapping during dev or build
The information in those page objects is used after the markdown has been compiled and during the Vue component rendering. The content is more there for reference and modifying it won't have the effect you are after.
This tripped me up as well.
All the markdown files are processed for inoformation, but then the actual compilation occurs through webpack. The general flow is:
.md -> markdown-loader -> vue-loader -> ...
My recommendation and what I have done is to create a custom webpack loader to modify the content before it goes through the VuePress markdown loader. I used this approach to run my markdown files through Nunjucks for templating pre-markdown compilation. It's increadibly easy to do this after you figure out the correct approach :) Here is a working approach:
config.js:
chainWebpack: config => {
config.module
.rule('md')
.test(/\.md$/)
.use(path.resolve(__dirname, './nunjucks'))
.loader(path.resolve(__dirname, './nunjucks'))
.end()
},
And then a simple loader can look like this(abridged):
module.exports = function(source) {
const rendered = nunjucks.renderString(source, config)
return rendered
}
I think that You should do this with use of extendMarkdown https://v1.vuepress.vuejs.org/config/#markdown-extendmarkdown.
Try like this
// index.js
module.exports = () => ({
name: 'vuepress-plugin-foo-bar',
extendMarkdown: md => {
const render = md.render;
md.render = (...args) => {
// original content
const html = render.call(md, ...args);
return 'new content';
};
},
});
You can to modify your .vuepress/config.js. For example, if you want to replace '---my text---' with 'MY TEXT' (with uppercase, for example) in all yours files markdown, you have to add the next code to .vuepress/config.js, into chainWebpack section where I use a regex expression:
// File: .vuepress/config.js
module.exports = {
...,
chainWebpack: config => {
// Each loader in the chain applies transformations to the processed resource:
config.module
.rule('md')
.test(/\.md$/)
.use("string-replace-loader")
.loader("string-replace-loader")
.options({
multiple: [{
search: '---(.*?)---',
replace: (match, p1, offset, string, groups) => `<div><p class="myclass">${p1.toUpperCase()}</p></div>`,
flags: 'ig'
},
{
search: ' ... ',
replace: (match, p1, p2, ..., pn, offset, string) => ` ... `,
flags: 'ig'
}
],
}, )
.end()
},
};

How to integrate cy.visit(local vs stage url) in a CI environment?

I have a test suite that opens a local url before making a bunch of assertions on some DOM elements.
Now I'd like to integrate Cypress in my CI/CD but I'm unsure on how to tell Cypress to visit the staging url instead of the local one.
Any idea?
it('The body of the page is in viewport', function() {
cy.visit(
'http://mylocalurltovisit.local.com.au:8666/foo'
);
let initialPosition;
const body = cy.get('body');
body.should('have.class', 'pdfView');
cy.get('.body-wrapper').should($el => {
initialPosition = $el.position();
expect(initialPosition.top).equal(0);
expect(initialPosition.left).equal(0);
});
});
I'd like for the visit url to automatically switch to say the staging one (which could be http://staging.com.au/foo) on the CI environment.
One way is to try creating the two different url's for local and staging sites as below in the cypress.json file.
{
"env": {
"project1_user": "admin",
"project1_password": "password",
"localSite" : {
"url" : "http://mylocalurltovisit.local.com.au:8666/foo"
},
"stagingSite" : {
"url" : "http://staging.com.au/foo"
}
}
}
Then receive the urls to const inside the test;
const localUrl = Cypress.env('localSite').url;
const stagingUrl = Cypress.env('stagingSite').url;
You can call in beforeEach or use directly inside the test. Same way you can use for staging site a well.
beforeEach( function() {
cy.visit(localUrl + '/somelogin.php' );
} );
Use an environment variable, but instead of putting it in the config file, pass it in the CI command that starts Cypress, e.g
cypress run --env TARGET=local
cypress run --env TARGET=staging
In the test, you can assign the correct url (once only), using a before().
describe('...', () => {
let url;
before(function() {
url = {
local: 'http://...',
staging: 'http://...'
}[Cypress.env('TARGET')];
})

Pdf2json integrated with Protactor

Has someone integrated pdf2json npm package with Protractor? I have been able to create a standalone node application to convert a PDF to json.
What I'm trying to do now is to add pdf2json to protractor.config.js and be able to use it in my test specs.
I managed to make it work myself so I thought to post what I did just in case someone needs the same.
Add the following to the Protractor config file
// PDF Parser
var PDFParser = require("pdf2json");
global.pdfParser = new PDFParser();
In the spec, we just need to wait for the async call to load the PDF to finish - note the done() (see Jasmine Async Support). The spec would look like:
var fs = require('fs');
describe('PDF Parser', function() {
it ("The spec", function(done){
// Capture the error
pdfParser.on("pdfParser_dataError", errData => {
console.error(errData);
done();
});
// Transform to json
pdfParser.on("pdfParser_dataReady", pdfData => {
fs.writeFile("path/to/save/json/file", JSON.stringify(pdfData));
done();
});
// This is an async call. We have to wait for it, so we use done in the 'it'
pdfParser.loadPDF("path/to/pdf/file");
});
});

Testing for file generation

I am writing a test suite for a file generation module. For example,
calling generator('test') will create a file in a specific directory named test. Here is how I set up mocha/chai tests.
let chai = require('chai');
let expect = chai.expect;
chai.use(require('chai-fs'));
let Promise = require('bluebird');
let rm = require('shelljs').rm;
let generator = Promise.promisify(require('../../commands/generator.js'));
describe('Generator', () => {
afterEach(() => {
rm('-rf', 'frontend');
});
it('should generate a file', () => {
Promise.resolve(generator('test', 'test', [])).then(() => {
expect('src/test.js').to.be.a.file();
});
});
});
I am using shelljs to remove the file after each test. However, I am running into an error
1) Generator "after each" hook:
Uncaught Error: EBADF: bad file descriptor, write
at Error (native)
I think this is caused by the fact that rm is an async function, but I'm not sure how to fix this issue.
There are multiple individual tests for the generator, and I want to remove the generated files after each test. Is there a better way to do this?

How can I include some libs in onPrepare startup function in protractor?

I need to do something more in onPrepare phase. For this startup code I need some external libraries. How can I include some files for onPrepare function?
My app need websql database in browser so I need add some fixtures: create tables and add some testing data in it. I wrote some small library for easy CRUD queries for websql db. So I don't want to write raw javascript for creating tables and append data.
Is any recommended way how can I achieve it?
For adding external libraries I just do
var env = require('./environment.js');
exports.config = {
seleniumAddress: env.seleniumAddress,
// Spec patterns are relative to this directory.
specs: [
'onPrepare/*_spec.js'
],
capabilities: env.capabilities,
baseUrl: env.baseUrl,
onPrepare: function() {
var UntrustedCertOverride = require('./untrusted-cert-override.js');
var OracleSsoPage = require('./claimsoverview/oracle-sso.po.js');
//Setup a global variable for using the non-angular driver
global.dvr = browser.driver;
dvr.get('/url');
UntrustedCertOverride(dvr);
var oracleSsoPage = new OracleSsoPage(dvr);
//etc.....
},
};
Or if you don't want your onPrepare: to be messy do this
var env = require('./environment.js');
// Configuration using a string in onPrepare to load a file with code to
// execute once before tests.
exports.config = {
seleniumAddress: env.seleniumAddress,
// Spec patterns are relative to this directory.
specs: [
'onPrepare/*_spec.js'
],
capabilities: env.capabilities,
baseUrl: env.baseUrl,
onPrepare: 'onPrepare/startup.js'
};
More Examples and Best practices can be found here: https://github.com/angular/protractor/tree/a368de0b74db35a90d9a2d8aa48a7e57a45a2aa7/spec