Skip to content

Commit 30d83e9

Browse files
authored
Overall improvements and new features (#229)
1 parent e8d68f9 commit 30d83e9

File tree

228 files changed

+7395
-1980
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

228 files changed

+7395
-1980
lines changed

.eslintignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
node_modules
22
.next
3+
cypress
34
src/**/*.test*
45
src/gql/**
56
src/propTypes/**

cypress/README.md

+38
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,44 @@ Read more about [the folder structure](https://docs.cypress.io/guides/core-conce
1010
- `screenshot`/`videos`: Will contain assets when tests fail, useful during development. When tests are executed by Github Actions, you'll find those assets under the "Artifacts" section. (e.g: https://github.com/UnlyEd/next-right-now/runs/862302266)
1111
- `fixtures`: Fixtures are used as external pieces of static data that can be used by your tests. (We've kept the fixtures installed during the Cypress initial install)
1212

13+
## Cypress config files
14+
15+
The files `cypress/config-*` are used for different purposes.
16+
17+
- `config-customer-ci-cd.json`: This file is a mock config file used by CI/CD GitHub Actions by the workflows `deploy-vercel-production.yml` and `deploy-vercel-staging.yml`.
18+
The `baseUrl` is a fake value (required by Cypress, but not used) which is replaced at runtime by the real `baseUrl` which is a dynamic Vercel deployment url.
19+
- `config-development.json`: This file is only used when running `yarn e2e:run` and `yarn e2e:open` locally.
20+
It uses `baseUrl=http://localhost:8888` which is where our local server is running. It's only meant for local testing
21+
- `config-$CUSTOMER_REF.json`: This file is only used when running `yarn deploy:$CUSTOMER_REF` locally. _It is not used by CI/CD workflows._
22+
1323
## Tests ordering
1424

1525
[Sanity Checks](./integration/app/_sanity/README.md) are executed first. Then, tests are executed by their folder/file alphabetical order by default.
26+
27+
## Resources about how to write tests better
28+
29+
- [[MUST WATCH!] Best Practices by the Author (2018) - 27mn](https://docs.cypress.io/examples/examples/tutorials.html#Best-Practices)
30+
- [Organize tests by type of devices (mobile/desktop)](https://docs.cypress.io/api/commands/viewport.html#Width-Height)
31+
- [Run tests on multiple subdomains](https://docs.cypress.io/faq/questions/using-cypress-faq.html#Can-I-run-the-same-tests-on-multiple-subdomains)
32+
- [Detect if Cypress is running](https://docs.cypress.io/faq/questions/using-cypress-faq.html#Is-there-any-way-to-detect-if-my-app-is-running-under-Cypress)
33+
- [Can my tests interact with Redux / Vuex data store? (AKA "Dynamic testing")](https://docs.cypress.io/faq/questions/using-cypress-faq.html#Can-my-tests-interact-with-Redux-Vuex-data-store)
34+
- [Check a custom property from the `window` object](https://docs.cypress.io/api/commands/window.html#Check-a-custom-property)
35+
- [Dynamic tests](https://github.com/cypress-io/cypress-example-recipes/tree/master/examples/fundamentals__dynamic-tests)
36+
- [Filters and data-driven tests](https://docs.cypress.io/examples/examples/tutorials.html#7-Filters-and-data-driven-tests)
37+
- [cypress-realworld-app](https://github.com/cypress-io/cypress-realworld-app/blob/develop/cypress/tests/ui/transaction-feeds.spec.ts)
38+
39+
## Officiel Cypress recommandations
40+
41+
> We see organizations starting with Cypress by placing end-to-end tests in a separate repo.
42+
> This is a great practice that allows someone on the team to prototype a few tests and evaluate Cypress within minutes.
43+
> As the time passes and the number of tests grows, we strongly suggest moving end-to-end tests to live right alongside your front end code.
44+
>
45+
> This brings many benefits:
46+
> - engages developers in writing end-to-end tests sooner
47+
> - keeps tests and the features they test in sync
48+
> - tests can be run every time the code changes
49+
> - allows code sharing between the application code and the tests (like selectors)
50+
51+
_[Source](https://docs.cypress.io/faq/questions/using-cypress-faq.html#What-are-your-best-practices-for-organizing-tests)_
52+
53+
[Cypress releases "Real World App" (RWA) - Blog post](https://www.cypress.io/blog/2020/06/11/introducing-the-cypress-real-world-app/)

cypress/config-customer-ci-cd.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"//": "This file is used by CI/CD GitHub Actions and not meant to be used locally",
2+
"//": "This file is used by CI/CD GitHub Actions (staging + production) and not meant to be used locally",
33
"baseUrl": "https://nrn-customer.vercel.app",
44
"projectId": "4dvdog",
55
"screenshotsFolder": "cypress/screenshots/customer",

cypress/config-development.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@
44
"screenshotsFolder": "cypress/screenshots/development",
55
"videosFolder": "cypress/videos/development",
66
"env": {},
7-
"ignoreTestFiles": "*.md"
7+
"ignoreTestFiles": "*.md",
8+
"experimentalComponentTesting": true
89
}

cypress/global.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/// <reference types="cypress" />
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
const baseUrl = Cypress.config().baseUrl;
22

33
describe('Sanity checks > Domain', {
4-
retries: {
5-
runMode: 2, // Allows 2 retries (for a total of 3 attempts) to reduce the probability of failing the whole tests suite because Vercel hasn't finished to deploy yet (which makes Cypress fail by trying to test the Vercel "waiting page", instead of our app)
6-
}
4+
// retries: {
5+
// runMode: 2, // Allows 2 retries (for a total of 3 attempts) to reduce the probability of failing the whole tests suite because Vercel hasn't finished to deploy yet (which makes Cypress fail by trying to test the Vercel "waiting page", instead of our app)
6+
// }
77
}, () => {
88
/*
99
* Visits the home page before any test
@@ -12,7 +12,7 @@ describe('Sanity checks > Domain', {
1212
cy.visit('/en');
1313
});
1414

15-
it('should be running on the right domain', () => {
15+
it(`should be running on the domain "${baseUrl}"`, () => {
1616
cy.url().then((url) => {
1717
cy.log(`Expected to be running on:`);
1818
cy.log(baseUrl);
@@ -22,3 +22,5 @@ describe('Sanity checks > Domain', {
2222
});
2323
});
2424
});
25+
26+
export {};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { Customer } from '../../../../src/types/data/Customer';
2+
import { CYPRESS_WINDOW_NS } from '../../../../src/utils/testing/cypress';
3+
4+
describe('Sanity checks > Browser data', () => {
5+
/**
6+
* Visits the home page before any test.
7+
*/
8+
before(() => {
9+
cy.visit('/en');
10+
});
11+
12+
/**
13+
* Prepare aliases before each test. (they're destroyed at the end of each test)
14+
*/
15+
beforeEach(() => {
16+
cy.prepareDOMAliases();
17+
});
18+
19+
it(`should have "window.${CYPRESS_WINDOW_NS}.dataset" defined`, () => {
20+
cy.get('@dataset').then((dataset) => {
21+
assert.isDefined(dataset);
22+
expect(Object.keys(dataset).length).to.be.greaterThan(1);
23+
});
24+
});
25+
26+
it(`should have "window.${CYPRESS_WINDOW_NS}.customer" defined`, () => {
27+
cy.get<Customer>('@customer').then((customer: Customer) => {
28+
assert.isDefined(customer.ref);
29+
});
30+
});
31+
});

cypress/integration/app/common/footer.js

-23
This file was deleted.
+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { Customer } from '../../../../src/types/data/Customer';
2+
3+
const baseUrl = Cypress.config().baseUrl;
4+
5+
describe('Common > Footer section', () => {
6+
/**
7+
* Visits the home page before any test.
8+
*/
9+
before(() => {
10+
cy.visit('/en');
11+
});
12+
13+
/**
14+
* Prepare aliases before each test. (they're destroyed at the end of each test)
15+
*/
16+
beforeEach(() => {
17+
cy.prepareDOMAliases();
18+
});
19+
20+
it('should have the Unly logo in the footer', () => {
21+
cy.get('#footer-logo-unly-brand').should('have.length', 1);
22+
});
23+
24+
it('should have the customer logo in the footer', () => {
25+
cy.get('#footer-logo').should('have.length', 1);
26+
});
27+
28+
it('should display the i18n button to change language', () => {
29+
cy.get<Customer>('@customer').then((customer: Customer) => {
30+
const availableLanguagesCount = 2;
31+
cy.log(`Available language(s): ${availableLanguagesCount}`);
32+
33+
if (availableLanguagesCount > 1) {
34+
it('should have a button to change the language which changes the language upon click', () => {
35+
cy.get('#footer-btn-change-locale').should('have.length', 1).click({ force: true });
36+
cy.url().should('eq', `${baseUrl}/fr`);
37+
});
38+
} else {
39+
it('should not have a button to change the language', () => {
40+
cy.get('#footer-btn-change-locale').should('not.have.length', 1);
41+
});
42+
}
43+
});
44+
});
45+
46+
});
47+
48+
export {};

cypress/integration/app/common/nav.js

-43
This file was deleted.

cypress/integration/app/common/nav.ts

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { Customer } from '../../../../src/types/data/Customer';
2+
3+
const baseUrl = Cypress.config().baseUrl;
4+
5+
describe('Common > Nav section', () => {
6+
/**
7+
* Visits the home page before any test.
8+
*/
9+
before(() => {
10+
cy.visit('/en');
11+
});
12+
13+
/**
14+
* Prepare aliases before each test. (they're destroyed at the end of each test)
15+
*/
16+
beforeEach(() => {
17+
cy.prepareDOMAliases();
18+
});
19+
20+
it('should have 3 links in the navigation bar', () => {
21+
cy.get('#nav .navbar-nav > .nav-item').should('have.length', 5);
22+
});
23+
24+
it('should have a link in the navbar that redirects to the home page', () => {
25+
cy.get<Customer>('@customer').then((customer: Customer) => {
26+
const isPageInEnglish = true;
27+
cy.get('#nav-link-home')
28+
.should('have.text', isPageInEnglish ? 'Home' : 'Accueil')
29+
.click();
30+
cy.url({ timeout: 10000 }).should('eq', `${baseUrl}/${isPageInEnglish ? 'en' : 'fr'}`);
31+
});
32+
});
33+
});
34+
35+
export {};
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,23 @@
11
const baseUrl = Cypress.config().baseUrl;
22

33
describe('Index page', () => {
4-
/*
5-
* Visits the home page before any test
6-
*/
4+
/**
5+
* Visits the home page before any test.
6+
*/
77
before(() => {
88
cy.visit('/en');
99
});
1010

11+
/**
12+
* Prepare aliases before each test. (they're destroyed at the end of each test)
13+
*/
14+
beforeEach(() => {
15+
cy.prepareDOMAliases();
16+
});
17+
1118
it('should display a main title', () => {
1219
cy.get('h1').should('have.length', 1).should('have.text', 'Next Right Now Demo');
1320
});
1421
});
22+
23+
export {};

cypress/plugins/index.js

+3
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@
1111
// This function is called when a project is opened or re-opened (e.g. due to
1212
// the project's config changing)
1313

14+
/// <reference types="cypress" />
15+
1416
module.exports = (on, config) => {
1517
// `on` is used to hook into various events Cypress emits
1618
// `config` is the resolved Cypress config
19+
return config;
1720
}

cypress/support/commands.d.ts

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
declare namespace Cypress {
2+
interface cy extends Chainable<undefined> {
3+
prepareDOMAliases: () => Chainable<Element>;
4+
}
5+
}

cypress/support/commands.js

+24-16
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,27 @@
77
// commands please read more here:
88
// https://on.cypress.io/custom-commands
99
// ***********************************************
10-
//
11-
//
12-
// -- This is a parent command --
13-
// Cypress.Commands.add("login", (email, password) => { ... })
14-
//
15-
//
16-
// -- This is a child command --
17-
// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
18-
//
19-
//
20-
// -- This is a dual command --
21-
// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
22-
//
23-
//
24-
// -- This will overwrite an existing command --
25-
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
10+
11+
import { CYPRESS_WINDOW_NS } from '../../src/utils/testing/cypress';
12+
13+
/**
14+
* Prepare DOM aliases by fetching the customer data from the browser window and aliasing them for later use.
15+
*
16+
* @example cy.prepareDOMAliases();
17+
*/
18+
Cypress.Commands.add('prepareDOMAliases', () => {
19+
return cy.window().then((window) => {
20+
cy.get('.page-wrapper').then(() => { // Wait for the DOM element to be created by Next.js before trying to read any dynamic data from the "window" object
21+
cy.log(`window[${CYPRESS_WINDOW_NS}]`, window[CYPRESS_WINDOW_NS]);
22+
23+
const {
24+
customer,
25+
dataset,
26+
} = window[CYPRESS_WINDOW_NS];
27+
28+
// Use aliases to make our variables reusable across tests - See https://docs.cypress.io/guides/core-concepts/variables-and-aliases.html#Sharing-Context
29+
cy.wrap(customer).as('customer');
30+
cy.wrap(dataset).as('dataset');
31+
});
32+
});
33+
});

cypress/support/index.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
// https://on.cypress.io/configuration
1414
// ***********************************************************
1515

16-
// Import commands.js using ES2015 syntax:
16+
// See https://dev.to/cuichenli/how-do-i-setup-my-nextjs-development-environment-2kao
17+
import 'cypress-react-unit-test/support';
1718
import './commands';
1819

1920
// See https://docs.cypress.io/api/events/catalog-of-events.html#Uncaught-Exceptions

cypress/tsconfig.json

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"extends": "../tsconfig.json",
3+
"include": [
4+
"./**/*.ts*"
5+
],
6+
"exclude": [],
7+
"compilerOptions": {
8+
"baseUrl": ".",
9+
"jsx": "react",
10+
"types": [
11+
"cypress"
12+
],
13+
"sourceMap": false,
14+
"isolatedModules": true
15+
}
16+
}

0 commit comments

Comments
 (0)