How can I pass google authentication using 'cypress run' command? - cypress

The module react-google-login is installed. I set browser profile. All operate as expected when I use : 'cypress open' command. But when I use 'cypress run' command than window for google authentication appears. What can I do for expected operating using 'cypress run' command?
module.exports = (on, config) => {
on('before:browser:launch', (browser = {}, launchOptions) => {
launchOptions.args.push('--user-data-dir=/home/user/.config/google-chrome/Default')
})
}
describe('Some text', async () => {
it ( 'Some text', () => {
cy.visit('https://my.site.com');
​...
}
}

Related

Cypress using actions from Pinia Vue3

I was learning some cypress from this video: https://www.youtube.com/watch?v=03kG2rdJYtc
I'm interested with he's saying at 29:33: "programatic login"
But he's using vue2 and Vuex.
My project is created with Vite and the state management is Pinia.
So how can I do a programatic login using the pinia action?
For example the welcome logged in user should see dashboard:
describe('Welcome', () => {
it('logged in user should visit dashboard', () => {
// login
cy.visit('/')
cy.url().should('contain', '/dashboard')
})
})
And my userStore:
export const useUserStore = defineStore({
id: 'user',
state: () => ({
username: ref(useLocalStorage('username', null)),
}),
getters: {
isLoggedIn: (state) => state.username !== null,
},
actions: {
login(username, password) {
return useAuthLoginService(username, password)
.then((response) => {
this.username = response.username
})
.catch((error) => {
return Promise.reject(new Error(error))
})
},
},
})
How can I call the login action on the cypress test?
For now as a workaround I'm writing on a localstorage like:
localStorage.setItem('username', 'user')
And it works fine, because userStore catch this item from localstorage and passes like it's logged in... But I don't like this solution, seems fragile, and I'd like to use the action which is made for login users.
Another thing I tried is adding the app variable inside window but it doesn't work for me... don't understand why...
on main.js
The video shows that code:
const vue = new Vue({...})
if(window.Cypress){
window.app = app
}
In my case it's:
const app = createApp(App)
if(window.Cypress){
window.app = app
}
But in cypress tests the window.app it's undefined... I don't know how I would access to userStore using this... like it was vuex.
Using the Pinia demo app as an example:
The store is initialized in App.vue. Add a reference to the newly created store(s) for Cypress to use
export default defineComponent({
components: { Layout, PiniaLogo },
setup() {
const user = useUserStore()
const cart = useCartStore()
if (window.Cypress) {
window.store = {user, cart) // test can see window.store
}
...
In the test
let store;
describe('Pinia demo with counters', () => {
beforeEach(() => {
cy.viewport(1000, 1000)
cy.visit(`http://localhost:${PORT}`)
.then(win => store = win.store) // get app's store object
})
it('works', () => {
cy.wait(500) // wait for the JS to load
.then(() => store.cart.addItem('Cypress test item')) // invoke action
.then(() => {
const item1 = store.cart.items[0] // invoke getter
cy.wrap(item1)
.should('have.property', 'name', 'Cypress test item') // passes
})
The login action is asynchronous, so return the promise to allow Cypress to wait.
// user.js
async login(user, password) {
const userData = await apiLogin(user, password)
this.$patch({
name: user,
...userData,
})
return userData // this returns a promise which can awaited
},
// main.spec.js
describe('Pinia demo with counters', () => {
beforeEach(() => {
cy.viewport(1000, 1000)
cy.visit(`http://localhost:${PORT}`).then(win => {
store = win.store
// default name in store before login
cy.wrap(store.user.name).should('eq', 'Eduardo')
// logging in
store.user.login('ed', 'ed').then(() => { // wait for API call
cy.wrap(store.user.name).should('eq', 'ed')
})
})
})
Alternatively, wait for the name to change on the page
// main.spec.js
cy.visit(`http://localhost:${PORT}`).then(win => {
store = win.store
// default name in store
cy.wrap(store.user.name).should('eq', 'Eduardo')
// logging on
store.user.login('ed', 'ed')
cy.contains('Hello ed') // waits for name on page to change
.then(() => {
cy.wrap(store.user.name).should('eq', 'ed')
})
})

Cypress: Why finicky in handling spec's after hook?

Blocked by a Cypress custom command not executing properly within a spec's after hook when a test failed. Experimented with the following minimal spec examples.
Context
Testing two Cypress custom commands:
cy.cmdLogin(): Standard username password authentication
cy.cmdLogout(): cy.get('logout_button').click()
Spec #1, cy.cmdLogout() in after hook does work
Very simple Cypress tests spec, test cy.cmdLogin() with after hook with cy.cmdLogout(), works perfectly:
context('SPEC Login', () => {
after('AFTER Logout', () => {
if (this.successLogin) {
cy.cmdLogout().then(() => {
cy.log('Logout Success');
});
}
});
it('TEST Login', () => {
cy.cmdLogin().then(() => {
cy.log('Login Success');
this.successLogin = true;
});
});
});
Spec #2, cy.cmdLogout() in after hook does not work
Modified Cypress tests spec, test cy.cmdLogin() with after hook with cy.cmdLogout(), now added a test that forced failure expect(false).to.be.a('boolean').to.be.true;. The after hook cy.cmdLogout() is called and fails:
Verified that Cypress could handle cy.get('logout_button') without failure.
Yet, extending with .click(), cy.get('logout_button').click(), Cypress would throw an error: Can not perform click() on undefined.
context('SPEC Login', () => {
after('AFTER Logout', () => {
if (this.successLogin) {
cy.cmdLogout().then(() => {
cy.log('Logout Success');
});
}
});
it('TEST Login', () => {
cy.cmdLogin().then(() => {
cy.log('Login Success');
this.successLogin = true;
});
});
it('TEST Fail', () => {
expect(false).to.be.a('boolean').to.be.true;
});
});
Spec #3, removed after hook, cy.cmdLogout() moved to another test does work
Modified Cypress tests spec again and removed after hook, test cy.cmdLogin(), test forced failure, and now test cy.cmdLogout(). This works perfectly:
context('SPEC Login', () => {
it('TEST Login', () => {
cy.cmdLogin().then(() => {
cy.log('Login Success');
this.successLogin = true;
});
});
it('TEST Fail', () => {
expect(false).to.be.a('boolean').to.be.true;
});
it('TEST Logout', () => {
if (this.successLogin) {
cy.cmdLogout().then(()=> {
cy.task('log', 'Logout Success');
});
}
});
});
Insight as to why Cypress is finicky with spec's after hook would be very much appreciated
The short answer (for the general situation) is change after() hook to before() hook, but in your case that wouldn't work because this.successLogin will always be undefined inside a before.
You would need to create a cy.isLoggedIn() command or call cy.cmdLogout() unconditionally.
To get after() the logic working on a fail, use
after('AFTER Logout', () => {
cy.cmdLogout()
});
Cypress.on('fail', (error, runnable) => {
cy.cmdLogout()
throw error
})
which is equivalent to this in plain javascript
try {
some-code-that-might-error()
cy.cmdLogout() // does not run if error occurs
} catch (error) {
cy.cmdLogout() // do the logout in error situation
}

Looking for a way tu use Cypress fixtures for all my custom commands outside an it block

I'm building some custom commands and trying to use my fixtures data for all my commands. Right now I'm forced to define it inside an it block.
Looks similar to this:
it("Commands", () => {
cy.fixture("fixtureFile").as("data");
cy.get("#data").then((data) => {
Cypress.Commands.add('login', () => {
cy.visit("/login");
cy.get('#login-email').type(data.userEmail);
cy.get('#login-pass').type(data.userPass, {log: false});
cy.get('.btn').debug().click();
})
Cypress.Commands.add('createTagMedia', () => {
cy.get(".close").click();
cy.get("#form-field-name").type(data.releaseVersion);
cy.get(".form-group-adTag > .CodeMirror > .CodeMirror-scroll").type(data.mediaTag);
cy.get("#media-save-btn").click();
})
})
})
This it block is being count as a test case, Is there a better way to pass this for more than one command at the same time?
The workaround I found was to put everything inside a before block, for example:
before(() => {
cy.fixture("fixtureFile").as("data");
cy.get("#data").then((data) => {
Cypress.Commands.add('login', () => {
cy.visit("/login");
cy.get('#login-email').type(data.userEmail);
cy.get('#login-pass').type(data.userPass, {log: false});
cy.get('.btn').debug().click();
})
Cypress.Commands.add('createTagMedia', () => {
cy.get(".close").click();
cy.get("#form-field-name").type(data.releaseVersion);
cy.get(".form-group-adTag > .CodeMirror > .CodeMirror-scroll").type(data.mediaTag);
cy.get("#media-save-btn").click();
})
})
})
Is there a reason why you won't use the following:
import {data} from '../fixtures/FixtureFile'
Considering you have the following JSON file:
{
"data": {
"userEmail": "blah",
"userPass": "blah",
"releaseVersion": "1"
}
}
You can include this on your tests, commands (Cypress.custom.commands), etc.
before(() => {
const data = cy.fixture("fixtureFile");
cy.login(data);
cy.createTagMedia(data);
})
You could literally do something like the above. With your Cypress.Commands in your command.ts or js whichever you're using.
And make the commands take in a parameter. Then the above before hook would just be in your tests.

How to run few test suit in browser and few in Nodejs environment Mocha?

I have scenario like this:
const isBrowser = new Function("try {return this===window;}catch(e){ return false;}");
if(isBrowser){
//Run browser specific code
}else {
// run nodejs specific code
}
I am setting up a test environment using Mocha, Chai and istanbul. How can i setup in such a way that few test suits should run on browser and few on NodeJs.
The goal is to get the combined coverage report.
how can I configure the Mocha to run in both browser and in NodeJs environment, using karma or without karma ?
for example:
//this should run in NodeJS Environment
describe('Loader Node Js', () => {
it('Load files from File system', (done) => {
loader.load('file path')
.then((files) => {
done();
});
});
});
//this should run in Browser Environment
describe('Loader Browser', () => {
it('Load files from Server AJAX', (done) => {
loader.load('file url')
.then((files) => {
done();
});
});
})
You should be able to test for Node.js and browser specific globals in your suite.
if (typeof module === 'object') {
//this should run in NodeJS Environment
describe('Loader Node Js', () => {
it('Load files from File system', (done) => {
loader.load('file path')
.then((files) => {
done();
});
});
});
} else {
//this should run in Browser Environment
describe('Loader Browser', () => {
it('Load files from Server AJAX', (done) => {
loader.load('file url')
.then((files) => {
done();
});
});
})
}
You have other options, too, like testing for typeof self === 'undefined', which is true in Node.js and false in a browser.
There are a few related question you may find interesting:
Environment detection: node.js or browser
How to check whether a script is running under node.js?
How to detect if script is running in browser or in Node.js?

Why am I receiving this warning in a cypress task?

I'm running a task to seed my database in a before hook. Cypress is complaining that
Cypress Warning: Cypress detected that you returned a promise in a test, but also invoked one or more cy commands inside of that promise.
Here is the task
import { seed } from '../../../src/server/db/seed'
const pluginHandler = on => {
on('task', {
'seed:db': () => {
return seed()
}
})
}
export default pluginHandler
This is the seed function
import { exec } from 'child_process'
import util from 'util'
const execP = util.promisify(exec)
export const seed = () => {
// Drop notes.
return execP('mongo starter_test --eval "db.notes.drop()"')
.then(async () => {
// Insert notes fixtures.
await execP(
'mongoimport --db starter_test --collection notes --file ./src/server/db/notes.json'
)
})
.then(() => {
return 0
})
}
And finally the test, which doesn't do anything yet
describe('My First Test', () => {
before(async () => {
await cy.task('seed:db')
})
it('Does not do much!', () => {
cy.visit(Cypress.env('HOST'))
})
})
I'm not, as far as I can see, using commands inside the promise as the warning suggests.
As far as I know, async should not be used inside Cypress before, or any other command (unless you install a 3rd-party lib).
Please try with before(() => cy.task('seed:db'))

Resources