On attempt to open simple app url in primitive Cypress (10.11) test (via Chrome browser) - blank page is loaded
cypress.config.js
const { defineConfig } = require("cypress");
module.exports = defineConfig({
chromeWebSecurity: false,
e2e: {
setupNodeEvents(on, config) {
// implement node event listeners here
},
"viewportWidth":1980,
"viewportHeight":1020,
"screenshotsFolder": "mochawesome-report/assets",
"videosFolder": "mochawesome-report/assets",
baseUrl: 'http://power-inspiration-1088-dev-ed.scratch.my.site.com/ebikes',
},
});
login.cy.js
describe('Sales Force First Test', () => {
it('Open page', () => {
cy.visit('/')
})
})
On attempt to open the same url in non-automation-controlled Chrome content is loaded. Firefox browser works perfectly for that site with Cypress (i.e. page content is loaded and rendered properly)
For now, it works only in Firefox. Cypress team is working on this spike to resolve this issue - https://github.com/cypress-io/cypress/issues/24290
Related
Concept
I'm using the concept of reusing the authentication state via storageState file in Playwright.
Following is my code snippet spread across different files:
Code
playwright.config.json
import type { PlaywrightTestConfig } from '#playwright/test';
import { devices } from '#playwright/test';
const config: PlaywrightTestConfig = {
testDir: './e2e',
reporter: 'html',
globalSetup: require.resolve('./e2e/global-setup.ts'),
use: {
storageState: './e2e/authStorageState.json',
},
projects: [
{
name: 'firefox',
use: {
...devices['Desktop Firefox'],
},
},
{
name: 'webkit',
use: {
...devices['Desktop Safari'],
},
},
{
name: 'chromium',
use: {
...devices['Desktop Chrome'],
},
},
],
};
export default config;
global-setup.ts
require('dotenv').config();
import { firefox, FullConfig } from '#playwright/test';
import E2EConstants from './e2e.constants';
const authenticateUser = async () => {
const browser = await firefox.launch();
const page = await browser.newPage();
await page.goto(`${process.env.BASE_URL}/${E2EConstants.LoginPage.URL}`);
await page
.getByLabel(E2EConstants.LoginPage.LABEL.EMAIL)
.fill(process.env.TEST_ADMIN_USERNAME as string);
await page
.getByLabel(E2EConstants.LoginPage.LABEL.PASSWORD)
.fill(process.env.TEST_ADMIN_PASSWORD as string);
await page.getByText(E2EConstants.LoginPage.BUTTON).click();
await page.waitForLoadState('networkidle');
await page.context().storageState({ path: './e2e/authStorageState.json' });
await browser.close();
};
async function globalSetup(_: FullConfig) {
await authenticateUser();
}
export default globalSetup;
This sets all the cookies in the authStorageState.json file. However, some cookies have domain as .b.com and some of them have the domain as a.b.com.
example.spec.ts
require('dotenv').config();
import { test, expect } from '#playwright/test';
test('homepage has same link', async ({ page }) => {
await page.goto(process.env.TEST_URL as string);
await expect(page).toHaveURL(process.env.TEST_URL as string);
});
The TEST_URL is the URL with domain a.b.com that is behind authentication and is accessible only when the user is signed in.
Problem
When I run the tests, I see that the Chromium test fails but the Firefox and Webkit tests pass. The test is unable to sign the user in on Chromium, unlike on other browsers. This is because the auth-related cookies (belonging to a.b.com domain) are not set on Chromium but they are set on other browsers. However, the non-auth-related cookies (belonging to .b.com domain) are set properly on all browsers.
What I tried
I tried manually setting the url of the cookies saved in authStorageState.json file to https://a.b.com after deleting the domain and path keys for the auth-related cookies and then when I run the tests, the Chromium test also passes.
The secure key for all cookies, which were not set, had the value as false. I tried manually changing the "secure": true for all cookies which were not set. Note that the sameSite property is set to "None". This change made the Chromium test cases to pass.
The sameSite key for all cookies, which were not set, had the value "None" while having "secure": false property. I tried manually removing the "sameSite": "None" property for all cookies which were not set. Note that I did not change the secure property. This change made the Chromium test cases to pass.
[Note that all the above points were independently implemented as workarounds and were not done together.]
Requirement
However, because the authStorageState.json file is to be created by default on first sign-in, I want to persist the same cookies that appear on browser instead of manually manipulating them. How can I get my Chromium test cases to pass?
Doubts
What is the difference between providing url instead of domain + path for Chromium?
Why do the cookies with subdomain a.b.com require secure property to be set to true and the Chromium-based test cases pass after this change?
Is removing the "sameSite": "None" for all the cookies with subdomain a.b.com right and why do the Chromium-based test cases pass after this change?
I'm working on a Cypress test for the Polish Wikipedia plugin, and I have this code in my cypress test:
Cypress.Commands.overwrite('visit', (orig, path, options) => {
return orig(`https://pl.wikipedia.org/${path}`);
});
Cypress.Commands.add('login', (pass) => {
cy.visit('/w/index.php?title=Specjalna:Zaloguj');
cy.get('#wpName1').type('<username>');
cy.get('#wpPassword1').type(pass);
cy.get('#userloginForm form').submit();
});
Cypress.Commands.add('dark_vector_wait', (pass) => {
cy.get('.vector-menu-content-list > li:nth-child(7)', { timeout: 10000 }).should('be.visible');
});
And in my spec:
describe('dark vector test', () => {
beforeEach('login', () => {
cy.login(Cypress.env('WIKI_PASSWORD'));
});
it('test discussion', () => {
cy.visit('/wiki/Dyskusja_wikipedysty:<username>');
cy.dark_vector_wait();
cy.matchImageSnapshot('discussion');
});
it('testing-page page', () => {
cy.visit('/wiki/Wikipedysta:<username>/testing-page');
cy.dark_vector_wait();
cy.matchImageSnapshot('testing-page');
});
});
And the second test is failing because as soon as Cypress type the password it automatically submits a form so cy.get('#userloginForm form').submit(); is executing after Cypress visits the home page (default redirect) and fail to find a form.
What's wrong? Why does Cypress auto-submit a form after a second time? This is not what Wikipedia is doing since the login form doesn't have any JavaScript code and you need to click login to be able to login to Wikipedia.
EDIT:
I've tried to
Use BaseURL and remove overwrite of visit.
Add type('{enter}'), but this only shows an error: cy.type() failed because this element is detached from the DOM.
EDIT 2
This is the screenshot of the action taken by cypress, second test has a page load without {enter} action and without form submit.
The problem is in Cypress.Commands.overwrite('visit').
You pass the parameter with a leading slash '/wiki/Dyskusja_wikipedysty:<username>' but concatinate to base which also has a trailing slash https://pl.wikipedia.org/${path}, so now the full path is
https://pl.wikipedia.org//wiki/Dyskusja_wikipedysty:<username>
If you set baseUrl in configuration, Cypress sorts it out for you
cypress.config.js
const { defineConfig } = require('cypress')
module.exports = defineConfig({
e2e: {
setupNodeEvents(on, config) {
// implement node event listeners here
},
baseUrl: 'https://pl.wikipedia.org'
}
})
Then remove the Cypress.Commands.overwrite('visit').
With these changes, I was able to pass both tests.
Using cy.session()
The problem might be one specific to locality, I do not have any steps missing in the Cypress log.
You can try adding a session cache so that the first login is re-used.
Cypress.Commands.add('login', (pass) => {
cy.session('login', () => { // login occurs only once
// credentials are cached
cy.visit('/w/index.php?title=Specjalna:Zaloguj');
cy.get('#wpName1').type('Jack Bosko');
cy.get('#wpPassword1').type(pass);
cy.get('#userloginForm form').submit();
// for good measure, confirm login was successful
// by checking for your name on the page
cy.contains('span', 'Jack Bosko')
})
})
So the problem was the weird IME keyboard that is part of MediaWiki. I somehow got it enabled on my system even when I was not logged in. Maybe added globally with cookies or something.
I noticed that keyboard when I was asking questions on the MediaWiki support page.
This is not related to Cypress. I'm investigating why the keyboard is there, and why clean environment it.
I have a page that is in break now, my backend is correcting it, but my test still returning success.
When I click to open the page, it is loading forever, doesn't open the page, and my "expect" was suppose to return an error, as it didn't find the "#btnStopService".
import 'testcafe';
import { Selector, ClientFunction } from 'testcafe';
fixture('Compass - Main page')
.page('http://localhost:3000/')
.beforeEach(async t => {
//login
t.ctx.username = 'admin';
t.ctx.password = 'admin';
await t.typeText(Selector('input').withAttribute('name','username'), t.ctx.username, {
paste: true,
replace: true,
});
await t.typeText(Selector('input').withAttribute('name','password'), t.ctx.password, {
paste: true,
replace: true,
});
await t.click(Selector('button').withAttribute('tabindex','0'));
})
.afterEach(async t => {
//logout
await t.click(Selector('#logoutBtn'));
});
test('Check if Services / Site Health page is loading... *** NOT WORKING ***', async t => {
await t.click(Selector('a').withExactText('Services'));
await t.click(Selector('a').withAttribute('href','#objectstore/sites/health'));
await t.expect(Selector('#btnStopService')).ok();
});
I am running it with:
testcafe edge .\test_spec.ts --selector-timeout 6000
The return I got is:
PS C:\ThinkOn\Compass_Test\Test1> testcafe edge .\test_spec.ts --selector-timeout 6000
Using locally installed version of TestCafe.
Running tests in:
- Microsoft Edge 17.17133 / Windows 10
Compass - Main page
√ Check if Services / Site Health page is loading... *** NOT WORKING ***
1 passed (23s)
PS C:\ThinkOn\Compass_Test\Test1>
Thanks all!!!
You need to add in an assertion option:
await t.expect(Selector('#btnStopService').exists).ok();
I want each of my tests to run on clean browser (Firefox) so i use restartBrowserBetweenTests:true option. Because i use non-Angular demo app, in onPrepare() function i use browser.waitForAngularEnabled(false). It's works fine, when i run a single spec, but when i run 2 specs, i have error.
Angular could not be found on the page. If this is not an Angular application, you may need to turn off waiting for Angular.
How can i solve this? And in addition, how onPrepare works in this case - every time when browser starts or one time before all specs?
Here is my conf.js
const screenshotReporter = require('./screenshotCustomReporter')
exports.config = {
capabilities: {
browserName: 'firefox'
},
restartBrowserBetweenTests: true,
framework: 'jasmine',
directConnect: true,
baseUrl: URL,
specs: ['path/**/*Spec.js'],
// Options to be passed to Jasmine.
jasmineNodeOpts: {
defaultTimeoutInterval: 30000,
includeStackTrace: true
},
onPrepare: () => {
require("#babel/register");
jasmine.getEnv().addReporter(screenshotReporter)
browser.waitForAngularEnabled(false)
}
}
You can recreate this issue using the following simple project:
conf.js
exports.config = {
framework: 'jasmine',
specs: ['./app.1.js', './app.2.js'],
seleniumAddress: 'http://localhost:4444/wd/hub',
restartBrowserBetweenTests:true,
onPrepare:function(){
browser.waitForAngularEnabled(false);
},
}
app.1.js
describe('second test', () => {
it('should check is displayed successfully', () => {
browser.driver.get("https://stackoverflow.com");
browser.driver.sleep(5000);
expect(element(by.linkText('Ask Question')).isDisplayed()).toBe(true);
});
});
app.2.js
describe('first test', () => {
it('should check is displayed successfully', () => {
browser.driver.get("https://stackoverflow.com");
browser.driver.sleep(5000);
expect(element(by.linkText('Ask Question')).isDisplayed()).toBe(true);
});
});
OnPrepare is defined for all settings need to be executed for suite. It means it
is always one time operation irrespective of number of spec files.
One concept you need to understand is that whenever the new instance of
firefox browser is launched then WebdriverJs initialize the instance of webdriver.
and global object browser in protractor also gets initialized.
In your case First spec file start firefox browser, OnPrepare function is executed afterwards and
default setting of protractor is overriden by WaitForAngularEnabled.But when you run second spec file,
again firefox browser is launched with fresh instance of webdriver and protractor browser which expect
angular application and in that case test case gets failed.
The solution for this problem is to use before function in spec file
describe('first test', () => {
before(() => {
browser.waitForAngularEnabled(false);
});
it('should check is displayed successfully', () => {
browser.driver.get("https://stackoverflow.com");
browser.driver.sleep(5000);
expect(element(by.linkText('Ask Question')).isDisplayed()).toBe(true);
});
});
Note : If you are using restartBrowserBetweenTests: true then you will have to use beforeEach() function for waitForAngularEnabled because every time fresh instance of webdriver will be created.
If this test:
'use strict'
describe('application', function() {
it('should login', function() {
browser().navigateTo('/');
expect('111').toBe('111');
});
});
includes the "browser" line, the result is:
Chrome 26.0 (Windows) application should login FAILED
ReferenceError: browser is not defined
at null.<anonymous> (...../test/js/e2e/tests/test.js:6:9)
Chrome 26.0 (Windows): Executed 1 of 1 (1 FAILED) (0.359 secs / 0.004 secs)
but without this line the test succeeds.
People suggest to include angular-scenario.js, but this breaks tests
expect('111').toBe('222');
is evaluated as true.
What to do?
You need to have your app's server running along with the karma runner. So with Node.js:
node app.js
Also, make sure you change the urlRoot property in your karma config so it doesn't conflict with your app's and yes you need angular-scenario.js
files = [
ANGULAR_SCENARIO,
ANGULAR_SCENARIO_ADAPTER,
'test/**/*_spec.js'
];
urlRoot = '/__karma/';
proxies = {
'/' : 'http://localhost:3000/'
};
I've run into this problem too. I believe you can only call browser() inside of a beforeEach(); Try this:
'use strict'
describe('application', function() {
beforeEach(function(){
browser().navigateTo('/');
});
it('should login', function() {
expect('111').toBe('111');
});
});