After reading the Cypress documentation on web security and when to disable it, I've decided I indeed need to do it. Is there a way to disable this just for one particular test/test suite? I'm using version 3.4.1 and this config is being set in cypress.json - therefore it's global for all tests.
Is there a way to disable web security just for one test? Thanks!
Original answer:
Does this work for you?
describe("test the config json", function () {
it("use web security false here", function () {
Cypress.config('chromeWebSecurity',false);
cy.visit("https://www.google.com");
console.log(Cypress.config('chromeWebSecurity'));
});
it("use web security true here", function () {
Cypress.config('chromeWebSecurity',true);
cy.visit("https://www.google.com");
console.log(Cypress.config('chromeWebSecurity'));
});
});
The config is changed as you can see from the console log.
See document here https://docs.cypress.io/guides/references/configuration.html#Cypress-config
Updates:
After I saw DurkoMatKo's comment I managed to find an URL to test this 'chromeWebSecurity' option. It did not work as expected.
I think changing this config might not work during running the same browser as this is more like a browser feature which will determine when start.
In this case what I can think of is only to run Cypress with different configurations.
The cypress doc here shows clear steps to do this.
hope this helps.
In my case it worked as follows.
the first thing was to set chromeWebSecurity to false
//cypress.json
{
"chromeWebSecurity": false
}
Then what I do is with a before assign it to true with Cypress.config
//cypress/integration/testing.spec.js
context('DEMO-01', () => {
beforeEach(function () {
Cypress.config('chromeWebSecurity', true);
});
describe('CP001 - start dasboard', () => {
it('P01: open dashboard', () => {
cy.visit(URL);
});
});
});
Related
I am running a test suite (test1, test2, test3) in Cypress. I noticed when I run this suite for the first time, they all pass. On the next test run, they fail. It seems that it is failing because the data from test3 has not been removed from the cypress browser when test1 begins to run. Could this be a load issue or would adding a logout command at the end of each test case be the solution? Not sure what would be the solution for this.
It seems that it is failing because the data from test3 has not been removed from the cypress browser. First step is to find out where that data being stored ex: cookie, session storage or local storage? Once you found it, you can choose appropriate method to delete data. Incase you are interested in clearing all u can do something as below
beforeEach(() => {
cy.window().then(win => win.sessionStorage.clear());
cy.clearCookies();
cy.clearLocalStorage();
});
Make sure above syntax is correct from cypress.io.
In your support/index.js or support/index.ts file, you could have a call to before(() => {} that loads your browser up once, and then have all of your tests run in one browser session. When you run the tests again, the first thing it will do is reload the page in the browser so that you have a clean session to start from.
Have you tried adding a step to clear the cookies?
Cypress.Commands.add('logout', () => {
cy.clearCookies().then(() => {
cy.getCookies().should('be.empty').visit('/')
})
});
I see 2 solutions:
I. Add an assertion for the logout command - usualy adding an assertion solves this problem.
Cypress.Commands.add("shopFE_logout", (domain) => {
cy.log('shopFE_logout - start')
cy.get('[data-qa="logoutButton"]')
.click({ force: true })
.should('not.exist')
cy.log('shopFE_logout - end')
})
II. The more secure, but not popular way - separate the test cases to be 1 per file and organize them in folders
I found this on cypress docs "Remember, Cypress already automatically clears localStorage, cookies, sessions, etc before each test. Make sure you are not trying to clean up state that is already cleaned up by Cypress automatically."
https://docs.cypress.io/guides/references/best-practices.html#Is-resetting-the-state-necessary.
I don't think adding logout before each test will be a good idea.
In my test, I do login before each test and I have not done logout and still working fine
describe('This is my test suite', () => {
let credential = "";
beforeEach(function () {
cy.visit(Cypress.env('devUrl'));
cy.fixture('data/common/loginData.json').then(loginData => {
credential = loginData;
});
})
it('This is my first testcase', () => {
cy.myLoginCommand(credential.valid.username, credential.valid.password)
objLocation.navToLocationPage()
objLocation.verifySuccessMsg('Successfully Saved')
})
it('This is my second testcase', () => {
cy.myLoginCommand(credential.valid.username, credential.valid.password)
objLocation.navToLocationPage()
objLocation.verifySuccessMsg('Successfully Updated')
})
})
Here is the scenario:
I'm logging to my application by visiting app.domain/login (example) which will redirect me to something like another.app.domain/
This is working fine. But when I logout: cy.contains('logout').click(), I am getting
CypressError: Timed out after waiting '60000ms' for your remote page to load.
Any suggestions to get around this issue? Ps: I just started to learn Cypress and I want to logout mainly because I want to restore the state back.. I don't want my environment to be updated/modified with the automation scripts. Thanks in advance.
First of all, in cypress there is no AfterAll hook, so we have to use more handmade solution. Second think: if you want to use cypress instead of curl to clean up after test is ok (but I recommend us curl ;)).
First you need to add line to scripts section in package.json:
{
"scripts": {
"after-cypress": "cypress run --spec cypress/hooks/after-all.js",
}
}
File cypress/hooks/after-all.js should looks like this:
describe('clean up after test', () => {
before(() => {
cy.login() // Should we save auth token here?
});
it('', () => {
cy
.request()
.request()
.request() // each of request should delete created during test data
});
});
When I press the "run all specs" button or use the run command that runs all files in Cypress it runs all test files alphabetically, so I don't want that.
I want to sort all of them with my own rules.
Let's say I have 3 steps in a chat app test.
Can connect the chat app
Can connect the chat
Can the user send a message
I want to test every step without being tied to each other.
What I mean, Testing one of their own function.
What I do is as follows
chat_app_connect.spec.js
describe('Server Connecting Test', () => {
it('Visit Server page', () => {
cy.visit('https://chat.page..');
});
it('Check welcome messages', () => {
cy.contains('Live Support');
cy.contains('Hello, Stranger');
});
it('Check URL and status of circle', () => {
// URL
cy.url()
.should('include', '/hello');
// Status Circle
cy.get('circle')
.should('have.class', 'positive');
});
});
chat_connect.spec.js
import './chat_app_connect.spec.js';
describe('Chat Connecting Test', () => {
it('Type customer name', () => {
cy.get('input')
.clear()
.type('E2E Test');
});
it('Click to the submit button', () => {
cy.get('.submit-button')
.click();
});
it('Check URL and status of circle', () => {
// URL
cy.url()
.should('equal', 'https://client.dev.octopus.chat/');
// Status Circle
cy.get('circle', { timeout: 5000 })
.should('have.class', 'positive');
});
});
chatting.spec.js
import './chat_connect.spec.js';
describe('Chatting Tests', () => {
it('Type a test message then press Enter and check the message if it sent', () => {
// Type
cy.get('#chat-message')
.clear()
.type('Hey I\'m a test message{enter}');
// Check the message
cy.get('.message-list')
.should('contain', 'Hey I\'m a test message');
});
});
as you see every test is tied to each other, and that is mean when I tried to test just catting functionality its call every test and the whole tests will be tested.
I don't know if it is the right way or not.
what should I do in this case or can it be an acceptable way
I have a particular case where I launch multiple instances of an app, rather than using fixtures or test data, I simply integrate user feedback as Cypress tests from login on forwards.
In any case, I used the specPattern config in cypress.json to set the spec file run order:
{
"baseUrl": "http://localhost:5000",
"specPattern": [
"login/*.js",
"leads/new-lead.spec.js",
"leads/leads-list.spec.js",
"leads/lead-detail.spec.js",
"leads/lead-modify.spec.js",
//...
]
}
No file numbering needed :D
The easiest solution is most likely to add a prefix to all your test files, such as:
01-chat_app_connect.spec.js
02-chat_connect.spec.js
etc.
Cypress is going to take those files in alphabetical order, which you can "trick" into your wanted behavior by using a number as a prefix.
Jean Lescure's answer was a lifesaver. We needed to run tests based on priority without having a bunch of duplicated tests or symlinks. The following worked for us in our default cypress config file:
"integrationFolder":"cypress/integration",
"testFiles": [
"high_priority_specs/**/*.js",
"medium_priority_specs/**/*.js",
"low_priority_specs/**/*.js"
]
To change the level of priority we used 3 configs files that were loaded using the cypress --configFile argument. To run the higher priority tests (smoke tests only) we used the following:
"integrationFolder":"cypress/integration",
"testFiles": [
"high_priority_specs/**/*.js"
]
Cypress does not intentionally let you do this, and for good reasons:
It's generally indicative of poor test design. Tests should not depend on the state of one another. Any test should be able to be run successfully in isolation from the rest of the test suite.
You'll never be able to take advantage of cypress' built in ability to run tests in parallel since you can't guarantee one spec will be ran after another
Here is a relevant discussion about this that gets into more detail: https://github.com/cypress-io/cypress/issues/390
However, if you decide to do this anyway, you can do it by prefixing the name of the specs with a number:
01-some-spec.js
02-alphabetically-first-spec.js
03-some-other-spec.js
In addition to #Brendan answer, if you have a nested folder structure, this approach will work as well.
01-folder-name
|
- 01-some-spec.js
I have a simple nodejs project that should load asynchronously the google maps api javascript, i followed this answer https://stackoverflow.com/a/15796543
and my app.js is like this:
var express = require("express"),
app = express(),
bodyParser = require("body-parser"),
methodOverride = require("method-override");
https = require("https");
requirejs = require('requirejs');
requirejs.config({
waitSeconds : 500,
isBuild: true,
paths : {
'async': 'node_modules/requirejs-plugins/src/async',
}
});
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(methodOverride());
var router = express.Router();
router.get('/', function(req, res) {
res.send("Hello World!");
});
requirejs(["async!http://maps.google.com/maps/api/js?key=mykey&sensor=false"], function() {
console.log(google);
});
app.listen(3000, function() {
console.log("asd");
});
package.json:
{
"name": "rest-google-maps-api",
"version": "2.0.0",
"dependencies": {
"express": "^4.7.1",
"method-override": "^2.1.2",
"body-parser": "^1.5.1",
"requirejs": "2.3.3",
"requirejs-plugins": "1.0.2"
}
}
i've got always the same error:
ReferenceError: google is not defined
The main issue here is that you are trying to run in Node code that is really meant to be used in a browser.
The async plugin
This plugin needs to be able to add script elements to document and needs window. I see you set isBuild: true in your RequireJS configuration. It does silence the error that async immediately raises if you do not use this flag, but this is not a solution because:
isBuild is really meant to be set internally by RequireJS's optimizer (or any optimizer that is compatible with RequireJS), not manually like you are doing.
isBuild means to indicate to plugins that they are running as part of an optimization run. However, your code is using the plugin at run time rather than as part of an optimization. So setting isBuild: true is a lie and will result in undesirable behavior. The async plugin is written in such a way that it effectively does nothing if isBuild is true. Other plugins may crash.
Google's Map API
It also expects a browser environment. The very first line I see when I download its code is this:
window.google = window.google || {};
Later in the code there are references to window.document and window.postMessage.
I don't know if it is possible to run the code you've been trying to load from Google in Node. I suspect you'd most likely need something like jsdom to provide a browser-like environment to the API.
assuming you did everything else correctly, which I am not testing here. The reason you are getting the error is because you call console.log(google) and there is no google variable. You need to pass google in as a reference in your call back function. This will either get rid of the error, or change the error if you have set up requirejs incorrectly.
requirejs(["async!http://maps.google.com/maps/api/js?key=mykey&sensor=false"],
function( **google** ) {
console.log(google);
});
see the requirejs docs http://requirejs.org/docs/node.html#1
For the URL to which a route applies I have a part defined in settings.json, like this
baseUrl: '/private'
My settings are published and accessible through the collections 'Settings' (on the client). So I tried the following:
Meteor.subscribe('settings');
Deps.autorun(function () {
var settings = Settings.findOne():
if (settings) {
Router.map(function () {
this.route('project', {
path: settings.baseUrl + '/:projectId,
controller: 'ProjectController'
});
});
}
});
The problem is that during initialisation the data is not yet on the client available, so I have to wait until the data is present. So far this approach doesn't work (yet). But before spending many hours I was wondering if someone has done this before or can tell me if this is the right approach ?
Updated answer:
I published solution in repository : https://github.com/parhelium/meteor-so-inject-data-to-html
. Test it by opening url : localhost:3000/test
In this case FastRender package is useless as it injects collection data in the end of head tag -> line 63.
Inject-Initial package injects data in the beginning of head tag -> line 106.
Needed packages:
mrt add iron-router
mrt add inject-initial
Source code:
Settings = new Meteor.Collection("settings");
if (Meteor.isClient) {
var settings = Injected.obj('settings');
console.log(settings);
Router.map(function () {
this.route('postShow', {
path: '/'+settings.path,
action: function () {
console.log("dynamic route !");
}
});
});
}
if (Meteor.isServer){
if(Settings.find().count() == 0){
Settings.insert({path:"test",data:"null"});
}
Inject.obj('settings', Settings.findOne());
}
Read about security in the bottom of the page : https://github.com/gadicc/meteor-inject-initial/
OLD ANSWER :
Below solution won't work in this specific case as FastRender injects data in the end of head tag. Because of that Routes are being initialized before injected data is present.
It will work when data from Settings collection will be sent together with html.
You can do that using package FastRender.
Create file server/router.js :
FastRender.onAllRoutes(function(path) {
// don't subscribe if client is downloading resources
if(/(css|js|html|map)/.test(path)) {
return;
}
this.subscribe('settings');
});
Create also publish function:
Meteor.publish('settings', function () {
return Settings.find({});
});
The above code means that if user open any url of your app then client will subscribe to "settings" publication and data will be injected on the server into html and available for client immediately.
I use this approach to be able to connect many different domains to meteor app and accordingly sent proper data.