Acceptance tests in Ember.js change the URL in the address bar - testing

This has been an annoying problem for days now. As I start to try to write acceptance tests for my Ember app, when I use the visit() function, the URL is changed in the browser's address bar, so when I change a bit of code and the liveReload happens, it navigates off my test page to whatever page I had told it to visit in the tests.
To troubleshoot, I ember new'd a new app, created a /home route and template, and created an acceptance test for it, and it passed fine, without changing the URL in the address bar. I've compared the code in tests/helpers and it's the same, as is tests/index.html.
I've searched all over without coming across an answer. It's been hard enough for me to grok testing, but problems like this are just tangential, but very irritating. If anyone has a tip as to why this is happening, I'd be extremely grateful for a fix.
As an example, here's my one acceptance test. It passes, but the URL actually changes:
import Ember from 'ember';
import { module, test } from 'qunit';
import startApp from 'star/tests/helpers/start-app';
var application;
module('Acceptance: AddMilestone', {
beforeEach: function() {
application = startApp();
},
afterEach: function() {
Ember.run(application, 'destroy');
}
});
test('Adding milestones', function(assert)
visit('/projects/1234567/details');
andThen(function() {
assert.equal(currentPath(), 'project.details');
});
});

Look in config/environment.js for a block similar to this:
if (environment === 'test') {
// Testem prefers this...
ENV.baseURL = '/';
ENV.locationType = 'none';
// keep test console output quieter
ENV.APP.LOG_ACTIVE_GENERATION = false;
ENV.APP.LOG_VIEW_LOOKUPS = false;
ENV.APP.rootElement = '#ember-testing';
}
Is ENV.locationType set to none for your test environment?
If not, are you changing the locationType elsewhere in your app? Setting it to none leaves the address bar alone.

Related

Cypress doesn't work with an external login

I'm working on e2e test with cypress on my application.
In my case the login are manage by a external service.
When I want to enter in my application's home page (https://myApplication/home), the system redirects me in different superdomains to login.
At first cypress seems to be able to change the superdomain, but once arrived in external service page for the authentication, the system go in login error (as if we have already logged in, but incorrect).
This type of behavior does not happen outside the cypress .
Are there alternative solutions to manage external access in a cypress test or is it possible to manage it directly from cypress?
I added in my cypress.json the chromeWebSecurity:false and when I call the link for login, I added the failOnStatusCode: false,
but it still doesn't work.
Assuming this is caused by SameSite cookie blocking , then I've just been fighting the same issue. I resolved it by intercepting all requests, checking if they had a set-cookie header(s) and rewriting the SameSite attribute. There's probably a neater way to do it, as this does clutter up the cypress dashboard a little.
Sadly Zachary Costa's answer no longer works as Chrome 94 removed the SameSiteByDefaultCookies flag.
You can add this as a command for easy reuse:
In your commands file:
declare namespace Cypress {
interface Chainable<Subject> {
disableSameSiteCookieRestrictions(): void;
}
}
Cypress.Commands.add('disableSameSiteCookieRestrictions', () => {
cy.intercept('*', (req) => {
req.on('response', (res) => {
if (!res.headers['set-cookie']) {
return;
}
const disableSameSite = (headerContent: string): string => {
return headerContent.replace(/samesite=(lax|strict)/ig, 'samesite=none');
}
if (Array.isArray(res.headers['set-cookie'])) {
res.headers['set-cookie'] = res.headers['set-cookie'].map(disableSameSite);
} else {
res.headers['set-cookie'] = disableSameSite(res.headers['set-cookie']);
}
})
});
});
Usage:
it('should login using third party idp', () => {
cy.disableSameSiteCookieRestrictions();
//add test body here
});
or alteratively, run it before each test:
beforeEach(() => cy.disableSameSiteCookieRestrictions());
We were encountering a similar issue, where Cypress was redirecting us to the default "You are not logged in" page after getting through the login process. I'm not certain if that's EXACTLY the issue you were experiencing, but just in case, here's our solution. In our case, the issue was caused by Chrome's "Same Site Cookies" feature interacting poorly with Cypress, so we needed to disable it. In your plugins/index.js file, you would add the following code:
module.exports = (on, config) => {
on('before:browser:launch', (browser, launchOptions) => {
if (browser.name === 'chrome') {
launchOptions.args.push('--disable-features=SameSiteByDefaultCookies');
}
return launchOptions;
});
};
Note that if you already have launchOptions being set, you can just add this code onto it so it doesn't clash at all.
Hopefully, this works for you as well!
In the current version of cypress you can't go to another domain in the same test. This is due to the fact that cypress injects its test into the browser (they are working on this issue).
So one solution today is that you need to utilize cy.request to perform the login programmatically and inject the auth secret (jwt, cookie, localstorage, token or what you have) into the browser context yourself (for cookie this would be cy.setcookie).
Always make sure to checkout the plugins if there is already an abstraction for your login. Often this is openId or ntlm.

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.

Ember acceptance test - timeouts

I have a reasonably special use-case:
I have an input field which issues a search when the user has stopped typing for 500ms. This is developed as a reusable add-on.
I would like to write an acceptance test for this but I cannot make the tests pass properly. The first passes, the second doesn't.
Now, the Ember runloop has a nice description but it's behaviour is really "something else".
This is my helper to timeout the runloop:
import Ember from 'ember';
export default Ember.Test.registerAsyncHelper('pauseFor', function (time) {
return Ember.Test.promise(function (resolve) {
Ember.run.later(resolve, time);
});
});
And this is how I use it
it('should do something after 500ms', function () {
visit('/');
fillIn('.search-input', 'a');
pauseFor(500);
andThen(function () {
// do my assertions/expectations here...
});
});
This is the error I get:
The weird thing is that I have 2 test cases and the first passes happily.
I guess my question is:
How to do this properly? What am I missing here or what am I doing wrong? How can I just simply timeout the test case?
Thanks for the halp!

How to perfectly isolate and clear environments between each test?

I'm trying to connect to SoundCloud using CasperJS. What is interesting is once you signed in and rerun the login feature later, the previous login is still active. Before going any further, here is the code:
casper.thenOpen('https://soundcloud.com/', function() {
casper.click('.header__login');
popup = /soundcloud\.com\/connect/;
casper.waitForPopup(popup, function() {
casper.withPopup(popup, function() {
selectors = {
'#username': username,
'#password': password
};
casper.fillSelectors('form.log-in', selectors, false);
casper.click('#authorize');
});
});
});
If you run this code at least twice, you should see the following error appears:
CasperError: Cannot dispatch mousedown event on nonexistent selector: .header__login
If you analyse the logs you will see that the second time, you were redirected to https://soundcloud.com/stream meaning that you were already logged in.
I did some research to clear environments between each test but it seems that the following lines don't solve the problem.
phantom.clearCookies()
casper.clear()
localStorage.clear()
sessionStorage.clear()
Technically, I'm really interested about understanding what is happening here. Maybe SoundCloud built a system to also store some variables server-side. In this case, I would have to log out before login. But my question is how can I perfectly isolate and clear everything between each test? Does someone know how to make the environment unsigned between each test?
To clear server-side session cache, calling: phantom.clearCookies(); did the trick for me. This cleared my session between test files.
Example here:
casper.test.begin("Test", {
test: function(test) {
casper.start(
"http://example.com",
function() {
... //Some testing here
}
);
casper.run(function() {
test.done();
});
},
tearDown: function(test) {
phantom.clearCookies();
}
});
If you're still having issues, check the way you are executing your tests.
Where did you call casper.clear() ?
I think you have to call it immediately after you have opened a page like:
casper.start('http://www.google.fr/', function() {
this.clear(); // javascript execution in this page has been stopped
//rest of code
});
From the doc: Clears the current page execution environment context. Useful to avoid having previously loaded DOM contents being still active.

How do you test AngularJS cookies in an end-to-end test?

I have a simple service that sets cookies in angular, but there's no obvious way to test that they've been set in an end-to-end test.
The code to test is as simple as
var splashApp = angular.module('splashApp', ['ngCookies']);
splashApp.controller('FooterController', function ($location, $cookies) {
$cookies.some_cookie = $location.absUrl();
});
But I can't find any docs on how to test. Here's what I have found:
How to access cookies in AngularJS?
http://docs.angularjs.org/api/ngCookies.$cookies
http://docs.angularjs.org/api/ngCookies.$cookieStore
I've also tried
angular.scenario.dsl('cookies', function() {
var chain = {};
chain.get = function(name) {
return this.addFutureAction('get cookies', function($window, $document, done) {
var injector = $window.angular.element($window.document.body).inheritedData('$injector');
var cookies = injector.get('$cookies');
done(null, cookies);
});
};
return function() {
return chain;
}
});
But this returns only the cookies for the parent browser, not the page I want to test.
Any examples on how to do this?
It seems like you need to use PhantomJS.
PhantomJS is a headless WebKit scriptable with a JavaScript API. It
has fast and native support for various web standards: DOM handling,
CSS selector, JSON, Canvas, and SVG. --PhantomJS website
It has support for custom cookies in its API.
In terms of testing, this is probably your best choice.
You might also want to look at CasperJS to help test page navigation.