How to run multiple tests under same fixture with same browser session - testing

I am trying to test multiple features in one test.js file with each feature implemented as a test. All these tests can be run only after login to the portal. Testcafe closes the browser after the first test which fails the subsequent tests. I used Role and .beforeEach so that the tests can log in to the portal and proceed as usual but is there any easy way to just continue all the tests in the same browser session without really closing them after each test?
I used Role feature and .beforeEach which looks like a workaround to me. Is there any other way to run all tests one after another without really closing the browser session. This will save us the login operation and the instability that might cause for each test.
import { Selector , ClientFunction, Role } from 'testcafe';
import loginpage from '../../features/blah/login/page-model'
const appPortalUser2 = Role('https://test.com', async t => {
await loginpage.signInToPortal()
await loginpage.login('test-userm', 'Password123')
}, { preserveUrl: true });
fixture `portal - settings`
.page `https://test.com/apps`
.beforeEach (async t => {
await t`enter code here`
.useRole(appPortalUser2)
});
test('settings', async t => {
//test1
await loginpage.foo1()
});
test('backup', async t => {
//test2
await loginpage.foo2()
});
Actual behavior: after test1 browser exits and login page appears which fails test2 without using .beforeEach.
Expected: browser session should continue for test2 after test1 without .beforeEach. Provide such an option where tests can continue without creating new browser sessions each time.

At the moment, there is no such option in the public API.
The idea is that one test should not affect another test in any way. If all tests had run in one browser session, every test would have influenced all preceding tests as it could have had a page with an invalid state.

Related

Testcafe Running Multiple Fixtures or Tests consecutively from within the same "file"

I've been working with Testcafe and have had some issues working with multiple "Fixtures" or "Tests" from within the same "file".
IE TESTFILE.js:
import { Selector, Role } from 'testcafe';
import Games from '../Pages/Games.js'
import {partnerAdmin} from '../Data/Roles.js'
const config = require('../Data/config.json')
fixture `Test Fixture #1`
.page `${config.envs.dev.baseUrl}`
test('Testing Test #1', async t => {
await t
.useRole(partnerAdmin)
await t
.expect(Selector(Games.gamesList).exists).ok()
.expect(Selector(Games.topRowGame).exists).ok()
.click(Games.gameActionsBtn)
});
fixture `Test Fixture #2`
.page `${config.envs.dev.baseUrl}`
test('Testing Test #2', async t => {
await t
.useRole(partnerAdmin)
await t
// DO OTHER STUFF
});
When I try this the 1st fixture will run perfectly... HOWEVER, the 2nd Fixture will stop at the login page. It's as if the 'useRole' function is failing the second time it's getting called.
I'm using the Roles as followed in the documentation.
IE Roles.js:
import { Selector, Role, t } from 'testcafe';
import Login from '../Pages/Login.js'
const config = require('../Data/config.json')
const partnerAdmin = Role(`${config.envs.dev.baseUrl}`, async t => {
await
Login.login(config.users.partnerAutoAdmin.name, config.users.partnerAutoAdmin.pass)
});
export { partnerAdmin }
It doesn't matter the order I place the "Fixtures" or "Tests" in. I can literally swap the 2nd fixture to before the 1st and it still does the same thing. The first runs, but the second just straight up fails as if it can't remember what it just did when it hits the login page the second time.
Anyone have any idea what I might be missing here? I'm using Roles and page models that seem to be working normally. Unfortunately the first test runs, but fails to continue on through the second for some reason even if I did just duplicate the same login/roles setup.

How can I log into my web app, then read through the records of my data.json file using TestCafe

I've googled and I can find how to loop through my data file. Apparently you run a test for each record of data.
I would like to have my single test log in, then cycle through each 'record' or item of the data file. The data is a series of searches in our app. So, the test would login and assert logged in then run those searches...
test('searches', async t => {
await t
// Log in...
.typeText('input[id="login-name"]', 'aguy')
.typeText('input[id="login-password"]', 'bbb')
.click('button[id="signin-button"')
.expect(Selector('span[id="logged-in-user"]').innerText).contains('Hal')
// At this point the app is ready to run through the searches doing this...
// forEach item in my data...
.typeText('input[id="simplecriteria"]', data.criteria)
.click('button[class="search-button"]')
.expect(Selector('div[class="mat-paginator-range-label"]').innerText).contains(data.srchResult)
});
TestCafe has test hooks, I recommend using them even though they are not that usuful in your case because TestCafe deletes cookies between tests, so if you log in once and then write your test like so:
const testData = require('../Resources/testData.json');
let executed = false;
fixture `Searches`
.page(baseUrl)
.beforeEach(async t => {
if (!executed) {
// run this only once before all tests
executed = true;
// log in
await t
.typeText('input[id="login-name"]', 'aguy')
.typeText('input[id="login-password"]', 'bbb')
.click('button[id="signin-button"')
.expect(Selector('span[id="logged-in-user"]').innerText).contains('Hal');
}
});
testData.forEach((data) => {
test('Searches', async t => {
await t
.typeText('input[id="simplecriteria"]', data.criteria)
.click('button[class="search-button"]')
.expect(Selector('div[class="mat-paginator-range-label"]').innerText).contains(data.srchResult);
});
});
then you'll most likely be logged out after the first test.
However, I'd still use beforeEach hook, but put the loop inside the test:
const testData = require('../Resources/testData.json');
fixture `Searches`
.page(baseUrl)
.beforeEach(async t => {
await t
// Log in...
.typeText('input[id="login-name"]', 'aguy')
.typeText('input[id="login-password"]', 'bbb')
.click('button[id="signin-button"')
.expect(Selector('span[id="logged-in-user"]').innerText).contains('Hal');
});
test('Searches', async t => {
testData.forEach((data) => {
await t
.typeText('input[id="simplecriteria"]', data.criteria)
.click('button[class="search-button"]')
.expect(Selector('div[class="mat-paginator-range-label"]').innerText).contains(data.srchResult);
});
});
There's obvious disadvantag:
many different searches are added as one test, so if one fails, the whole "searches" test case will be marked as failed
Another solution might be to find out what it means to be logged in. If it's about adding some cookie, you might log in once and then only set up the cookie before your tests. However, in many modern systems, such "log-in cookies" will have httpOnly flag, so you can't really set it in JavaScript.

TestCafe expect() scope

I'm new to TestCafe and I am struggling to achieve a test in a "clean way" using roles.
I've been reading the docs from TestCafe and got insipired by this "tutorial" https://github.com/qualityshepherd/testcafe-example for creating my tests.
I have to write a test that tests if a user is correctly logged in. For that I want to use Roles. This is the architecture of my test scripts and I will explain why I do this choices:
data
roles.js this is where I defined the Roles
pages (folder)
base-page.js the wrapper of all pages (where I have content like toolbar/navbar/...
login-page.js my login page
status-page.js the page after a successful login
tests
login.test.js the test that doesn't work like I want to
Let's dive into some pieces of code.
This is my login.test.js file:
import basePage from '../../pages/base-page';
fixture `Account management`;
test('Login with valid account',
async (t) => {
await t
.useRole(validUser) // this logs me in using a Role defined in another file
.expect(basePage.userSection.exists).ok() // check if exists
.expect(basePage.userSection.child(1).textContent).eql('somevalid.username'); // check if value is the one I expect
}
);
// other tests...
The roles.js file:
import { Role } from 'testcafe';
import loginPage from '../pages/login-page.js';
// I will obviously not display any valid data on this post
export const validUser = Role(
loginPageUrl,
async (t) => {
await loginPage.login(t, someValidEmail, someValidPassword);
},
{ preserveUrl: true }
);
My base-page.js file:
// imports
const baseUrl = 'http://localhost:3000/';
const basePage = {
baseUrl,
userSection: $('div.navigation-account-dropdown.js-user-menu'), // some section on my navbar
[... other stuff you don't need to know about]
}
export default basePage;
My login-page.js file:
import { Selector as $, t } from 'testcafe';
import basePage from './base-page';
// FIXME: I do not want to use .expect(...) here but in the proper test file (login.test.js)
const loginPage = {
url: `${basePage.baseUrl}signin`,
usernameInput: $('#at-field-email'),
passwordInput: $('#at-field-password'),
loginBtn: $('#at-pwd-form').find('button'),
async login(t, username, password) {
await t
.typeText(this.usernameInput, username)
.typeText(this.passwordInput, password)
.click(this.loginBtn) // I want to stop the logic here and do the assertion on the proper test.js file
.expect(basePage.userSection.exists).ok(); // I do not want to do assertions here but I have to do it otherwise tests fails
}
}
export default {
...basePage,
...loginPage
};
The problem is that when I comment the line .expect(basePage.userSection.exists).ok(); in the login-page.js file the test doesn't run as expected. It fills the form, clicks submit and then nothing happens (I am still in login page, it is reloaded). So I added one assertion here to make it "work". But I don't want any assertion here, it shouldn't be here but in the actual test file.
My main question is: how can I remove any assertion from my login-page.js file. Why do I have to do that .expect there in order for the test to pass?
Do you have any tips/advices regarding how to structure a project test (I have to write a lot of tests so that's why I am trying to separate some logics)?
The behavior you described is unexpected. As a possible solution, use the preserveUrl option: https://devexpress.github.io/testcafe/documentation/test-api/authentication/user-roles.html#optionspreserveurl.
If it does not help, we need to research the problem in detail. Please share your example and create a separate issue in the TestCafe github repository using the following form: https://github.com/DevExpress/testcafe/issues/new?template=bug-report.md.
If you cannot share your project here, you can send it to support#devexpress.com.
Please note that our policy prevents us from accessing internal resources without prior written approval from a site owner. If you want us to further research the problem directly on your side, please ask the website owner to send us (support#devexpress.com) written confirmation. It must permit DevExpress personnel to remotely access the website and its internal resources for research/testing/and debugging purposes.

testcafe - CAS authentication

New to TestCafe. Got some simple example tests working easily. However, I wasn't able to find any examples of features that would seem to allow me to log in to my application via a CAS authentication page.
This code works to find the login button on the login page and click it.
fixture`VRT`
.page `http://myapp.com/login/`;
test('Find Login button', async t => {
const input = Selector('input.btn');
await t.click(input);
});
And this would work to type in the username and password on the login page:
test('Login using CAS', async t => {
await t
.expect("#username").exists
.typeText('#username', 'myuser')
.typeText('#password', '***')
.click('#submit');
});
But the problem is that there seems to be no way to continue from one test to another. So I can't go from the first test that opens the login page, to the next test that enters the credentials. It seems like, for TestCafe, every test has to specify its own page.
If I try to go to the CAS login page directly, by specifying it as the fixture "page", TestCafe fails to open the page, I think because the URL is extremely long.
Thanks for any suggestions.
Update:
So, using roles got me a bit further (thanks) but had to get through one more CAS page with an input button to click before getting to the page I wanted to test. Was able to add in another click to the role login:
import { Role } from 'testcafe';
import { Selector } from 'testcafe';
const finalLoginBtn = Selector('input.btn');
const myUserRole = Role('http://example.com/login', async t => {
await t
.click('input.btn')
.typeText('#username', 'my-username')
.typeText('#password', '*****')
.click('#submit')
.click(finalLoginBtn);
}, { preserveUrl: true });
fixture`VRT`
test("My User Page", async t => {
await t.navigateTo(`http://example.com`)
await t.useRole(myUserRole);
});
The TestCafe 'User Roles' functionality should suit your requirements. Please, refer to the following topic in the TestCafe documentation for details: https://devexpress.github.io/testcafe/documentation/test-api/authentication/user-roles.html

Testcafe role problems with logout

I use the Testcafe roles in my script to avoid new logins each test inside a fixture. It works really good, but I run into a problem where I cannot find any solution.
If I use the role to log in into the page and I use the common page functions to logout of the page, it means to click the selector Logout, then the upcoming script still thinks that the role is still logged in and the test fails.
As sample:
test('Login und Logout', async t => {
const loginheader = Selector('.login-header')
await t
.useRole(bc3Tester)
.navigateTo(inputStore.metaUrl)
.click(Selector('.cl-sidemenu-button'))
.click(Selector('.side-menu-logout.cl-asset-button-animation'))
await t
.wait(2000)
.expect(loginheader.exists).ok()
});
If I run this test a second time it will fail at
.click(Selector('.cl-sidemenu-button'))
because the role script not work (No auto login again)
const bc3Tester = Role(inputStore.metaUrl, async t => {
await t
.typeText(Selector('#email'), inputStore.clLogin, {
caretPos: 0
})
.typeText(Selector('#password'), inputStore.clPassword, {
caretPos: 0
})
.click(Selector('span').withText('Login'))
});
Because it will not log in. Any idea?