CypressIO tests to be executed in multiple URL - automation

An Ecommerce website have different URL's for different countries. And each of these website have to be tested. We have set of automation scripts written in CYPRESSIO. Looking for ideas how these scripts can be rerun for different URL's.
Example URL's for different countries
UK: https://www.abc.co.uk
CH: https://www.abc.ch
DE: https://www.abc.de
There are some functionalities which are country specific and hence we have to run tests for all the URL's . Any ideas and leads would be appreciated.
Thanks in advance :)

Set the tests inside a data loop
const urls = ['https://www.abc.co.uk', 'https://www.abc.ch', 'https://www.abc.de'];
urls.forEach(url => {
describe(`Testing url: ${url}`, () => {
before(Cypress.config('baseUrl', url))
it('...', () => {
})
})
Testing a simplified scenario,
support/index.js
beforeEach(() => {
console.log('beforeEach in support', Cypress.config('baseUrl'))
})
dynamic-baseUrl.spec.js
const urls = ['https://www.abc.co.uk', 'https://www.abc.ch', 'https://www.abc.de'];
urls.forEach(url => {
describe(`Testing url: ${url}`, () => {
before(() => Cypress.config('baseUrl', url))
it('sees the required baseURL', () => {
console.log('it', Cypress.config('baseUrl'))
})
})
})
console output
beforeEach in support https://www.abc.co.uk
it https://www.abc.co.uk
beforeEach in support https://www.abc.ch
it https://www.abc.ch
beforeEach in support https://www.abc.de
it https://www.abc.de
Cypress log
Testing url: https://www.abc.co.uk
...passed
Testing url: https://www.abc.ch
...passed
Testing url: https://www.abc.de
...passed

One way would be to create country-specific cypress.json files.
For eg. cypress-de.json
Inside it you can define baseURL and country-specific test cases using testFiles[] as:
{
"testFiles": [
"TC_01.spec.js",
"TC_02.spec.js",
"TC_03.spec.js",
"TC_04.spec.js"
],
"baseUrl": "https://www.abc.de"
}
Now when you want to run tests, all you have to do is pass the relevant cypress.json file through CLI using the command:
npx cypress run --config-file cypress-de.json

Related

nestjsx-automapper profiles not found in nestjs test modules

Im trying to make unit testing for some features in my NestJS API.
I make use of nestjsx-automapper. The exception happens only while testing any service that uses mappings. I use #nestjs/testing and jest.
I have managed to replicate the error in this test:
describe('UserController', () => {
let userServiceInterface: UserServiceInterface;
beforeEach(async () => {
const app: TestingModule = await Test.createTestingModule({
imports: [AutomapperModule.withMapper('user')],
providers: [
{
provide: 'UserServiceInterface',
useClass: UserService,
},
],
}).compile();
userServiceInterface = app.get<UserServiceInterface>(
'UserServiceInterface',
);
});
describe('find', () => {
it(`should return user with name 'pipo'`, () => {
const user = userServiceInterface.find('pipo');
expect(user.username).toBe('pipo');
});
});
});
I've been looking for different ways to configure profiles, but importing them is the only way I've found.
Thanks in advance.
I figured out how nestjsx-automapper load profiles.
As you can't provide profiles in modules, you depend on that the application runs the profiles first, and it is handled by AutomapperModule from nestjsx-automapper.
I don't know why nest Modules load AutomapperModule and consequently the profiles, but Test Modules doesn't, I will have to work more with nest to understand it.
To tell Test Modules to load AutomapperModule (as they don't by default) on init, use:
moduleRef.get(AutomapperModule).onModuleInit();

Jest + puppeteer best architecture practices

I just entered the world of testing with puppeteer and jest, and I was wondering what the best practice was in terms of folder architecture and logic.
I've never done testing before and I think I'm getting a little lost in the different principles and concepts and how it all fits together.
I learned to do my tests based on the page-object model, so I have classes for each of my pages, but also for each of my modules ( or components ). For example, in my application, the header or the login modal are components.
Then I have a test file per page or per component.
(for example the landingPage.tests.js file, which uses the model of the LandingPage class in the LandingPage.js file)
Here is a concrete example:
I have different login cases and I'd like to test them all. For example I want to test to connect with a "normal" user, for which the process is simply login then password. Then I need to test with a user who has activated 2FA, or with a user from a company that uses SSO.
I first thought about putting my different tests in authentication.tests.js, in different describe blocks, thinking it would open a new tab each time, but it doesn't... I use puppeteer in incognito mode to make sure each tab is an isolated session.
So my questions are:
Where is the best place to do these test suites?
Am I supposed to have test files that "describe" the pages ( for example, the button must be present, such text must be here etc) and also have "scenario type" test file ( a set of contextual actions to a user, like for my different login cases) ?
Here is authentication.tests.js, in which I would like to tests all my different ways of logging in :
import HeaderComponent from "../../../pages/components/HeaderComponent";
import AuthenticationComponent from "../../../pages/components/AuthenticationComponent";
import LandingPage from "../../../pages/landing/LandingPage";
import {
JEST_TIMEOUT,
CREDENTIALS
} from "../../../config";
describe('Component:Authentication', () => {
let headerComponent;
let authenticationComponent;
let landingPage;
beforeAll(async () => {
jest.setTimeout(JEST_TIMEOUT);
headerComponent = new HeaderComponent;
authenticationComponent = new AuthenticationComponent;
landingPage = new LandingPage;
});
describe('Normal login ', () => {
it('should click on login and open modal', async () => {
await landingPage.visit();
await headerComponent.isVisible();
await headerComponent.clickOnLogin();
await authenticationComponent.isVisible();
});
it('should type a normal user email adress and validate', async () => {
await authenticationComponent.typeUsername(CREDENTIALS.normal.username);
await authenticationComponent.clickNext();
});
it('should type the correct password and validate', async () => {
await authenticationComponent.typePassword(CREDENTIALS.normal.password);
await authenticationComponent.clickNext();
});
it('should be logged in', async () => {
await waitForText(page, 'body', 'Success !');
});
});
describe('SSO login ', () => {
// todo ...
});
});
Thank you and sorry if it sounds confusing, like I said I'm trying to figure out how it all fits together.
Regarding the folder structure, Jest will find any files according to the match config, basically anything called *.spec.js or *.test.js. Looks like you know that already.
What that means is the folder structure is completely up to you. Some people like to have the tests for components in the same folders as the components themselves. Personally I prefer to have all the tests in one folder as it makes the project look cleaner.
The other benefit of having all the tests in one folder is that you can then start to distinguish between the types of tests. Component tests check that pure components render and operate as expected. You don't need Puppeteer for this, use snapshots if you're in a React app. Puppeteer is good for integration tests that navigate through so-called 'happy paths', login, signup, add to cart etc., using a headless Chromium browser.
To answer the specific problem you have been having with Jest / Puppeteer on a new page for each test:
//keep a reference to the browser
let browser
//keep a reference to the page
let page
// open puppeteer before all tests
beforeAll(async () => {
browser = await puppeteer.launch()
})
// close puppeteer after all tests
afterAll(async () => {
await browser.close()
})
// Get a new page for each test so that we start fresh.
beforeEach(async () => {
page = await browser.newPage()
})
// Remember to close pages after each test.
afterEach(async () => {
await page.close()
})
describe('Counter', () => {
// "it" blocks go here.
})
Hope that helps a bit.

I want to persist/preserve login in all spec files instead of logging in again and again for every file

I want to persist/preserve login in all spec files instead of logging again and again for every file: I found solution for multiple tests is a single file but I need solution for multiple tests for multiple specs files
Save local storage across tests to avoid re-authentication
Set local storage in Cypress
https://www.npmjs.com/package/cypress-localstorage-commands
These are solutions for single file with multiple tests but I need solution for multiple files
The cypress-localstorage-commands package persists localStorage even between different tests files, this is its default behavior. As it describes in its documentation, it also provides a clearLocalStorageSnapshot command to clean the localStorage between test files if needed.
Note that you'll have to restore the localStorage in every test file using the cy.restoreLocalStorage() command:
describe("Tests file 1", () => {
beforeEach(() => {
cy.restoreLocalStorage();
});
afterEach(() => {
cy.saveLocalStorage();
});
});
describe("Tests file 2", () => {
beforeEach(() => {
cy.restoreLocalStorage();
});
afterEach(() => {
cy.saveLocalStorage();
});
});

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')];
})

TestCafe 'dynamic' tests cases

I created a few e2e sanity tests for my current project using TestCafe. These tests are standard TestCafe tests:
fixture(`Basic checkout flow`)
test('Main Flow', async (t) => {
});
I would like to execute this test for multiple site locales and for multiple channels. i.e. I need this test to run for nl_nl, nl_be, en_gb, .. and also for channels like b2c, b2b, ...
The easiest way is to create a loop in the test itself to loop over the locales and channels, but I want to run these test concurrently.
I tried to create a function to dynamically generate these tests, but TestCafe can't seem to detect the tests then.
dynamicTest('Main Flow', async (t) => {
});
function dynamicTest(testName, testFn) => {
const channels = ['b2c']
channels.forEach((channel) => {
test(`[Channel] ${channel}] ${testName}`, testFn);
});
};
Is there a better way of doing this? The only solution I see is running the test script multiple times from Jenkins to have concurrency.
more detailed code:
import HomePage from '../../page/HomePage/HomePage';
import EnvUtil from '../../util/EnvUtil';
const wrapper = (config, testFn) => {
config.locales.forEach(async locale =>
config.channels.forEach(async channel => {
const tstConfig = {
locale,
channel
};
tstConfig.env = EnvUtil.parse(tstConfig, config.args.env);
testConfig.foo = await EnvUtil.get() // If I remove this line it works!
testFn(config, locale, channel)
})
);
};
fixture(`[Feature] Feature 1`)
.beforeEach(async t => {
t.ctx.pages = {
home: new HomePage(),
... more pages here
};
});
wrapper(global.config, (testConfig, locale, channel) => {
test
.before(async (t) => {
t.ctx.config = testConfig;
})
.page(`foo.bar.com`)
(`[Feature] [Locale: ${locale.key}] [Channel: ${channel.key}] Feature 1`, async (t) => {
await t.ctx.pages.home.header.search(t, '3301');
.. more test code here
});
});
If I run it like this I get a "test is undefined" error. Is there something wrong in the way I'm wrapping "test"?
Using TestCafe of version 0.23.1, you can run tests imported from external libraries or generated dynamically even if the test file you provide does not contain any tests.
You can learn more here: Run Dynamically Loaded Tests