⚙️ setup cypress & visual testing workflow

pull/82/head
nicgirault 2 years ago
parent 341bf89b28
commit 377e3bcd10

@ -5,15 +5,54 @@ jobs:
- image: cimg/node:lts - image: cimg/node:lts
steps: steps:
- checkout - checkout
- restore_cache:
name: Restore Yarn Package Cache
keys:
- v1-node_modules-{{ checksum "yarn.lock" }}
- run: - run:
name: update-yarn name: Install Dependencies
command: 'curl --compressed -o- -L https://yarnpkg.com/install.sh | bash' command: yarn install --frozen-lockfile
- save_cache:
name: Save Yarn Package Cache
key: v1-node_modules-{{ checksum "yarn.lock" }}
paths:
- node_modules
- ~/.cache
background: true
- run: - run:
name: install-dependencies name: build app
command: yarn install command: yarn build
- run: - run:
name: compile-translations name: integration tests
command: yarn translate command: npx http-server build --port 3000 --silent & yarn test:integration
- run: - run:
name: test name: Store failing screenshots
command: yarn test command: |
mkdir /tmp/artifacts
if [[ -n $(find cypress/screenshots -name '*\(failed\)*.png') ]]
then
find cypress/screenshots -name '*\(failed\)*.png' -print0 | xargs -0 mv -t /tmp/artifacts
fi
when: on_fail
- store_artifacts:
path: /tmp/artifacts
- run:
name: Upload screenshots
command: |
for filename in cypress/screenshots/**/**/*
do
newname=$(echo $filename | sed -e 's/ (attempt .)//')
if [ "$filename" != "$newname" ]
then
mv "$filename" "$newname"
fi
done
ARGOS_COMMIT=$CIRCLE_SHA1 ARGOS_BRANCH=$CIRCLE_BRANCH yarn argos
background: true

2
.gitignore vendored

@ -34,3 +34,5 @@ yarn-error.log*
.netlify .netlify
functions/next_* functions/next_*
.env .env
cypress/screenshots

@ -0,0 +1,4 @@
{
"baseUrl": "http://localhost:3000",
"video": false
}

@ -0,0 +1,3 @@
{
"todo": true
}

@ -0,0 +1,23 @@
/// <reference types="cypress" />
describe("signup flow", () => {
beforeEach(() => {
// Cypress starts out with a blank slate for each test
// so we must tell it to visit our website with the `cy.visit()` command.
// Since we want to visit the same URL at the start of all our tests,
// we include it in our beforeEach function so that it runs before each test
cy.visit("/");
});
it("allows to fill an election form", () => {
cy.get('[data-test="question-input"]').type(
"Pour faire gagner lécologie et la justice sociale à lélection présidentielle, jestime que chacune de ces personnalités serait…"
);
cy.screenshot();
cy.get('[data-test="question-input"]').type("{enter}");
cy.url().should("include", "/new?title=");
cy.screenshot();
});
});

@ -0,0 +1,22 @@
/// <reference types="cypress" />
// ***********************************************************
// This example plugins/index.js can be used to load plugins
//
// You can change the location of this file or turn off loading
// the plugins file with the 'pluginsFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/plugins-guide
// ***********************************************************
// This function is called when a project is opened or re-opened (e.g. due to
// the project's config changing)
/**
* @type {Cypress.PluginConfig}
*/
// eslint-disable-next-line no-unused-vars
module.exports = (on, config) => {
// `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config
}

@ -0,0 +1,25 @@
// ***********************************************
// This example commands.js shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add('login', (email, password) => { ... })
//
//
// -- This is a child command --
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })

@ -0,0 +1,20 @@
// ***********************************************************
// This example support/index.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************
// Import commands.js using ES2015 syntax:
import './commands'
// Alternatively you can use CommonJS syntax:
// require('./commands')

@ -0,0 +1,8 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["es5", "dom"],
"types": ["cypress"]
},
"include": ["**/*.ts"]
}

@ -4,9 +4,11 @@
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "next dev", "dev": "next dev",
"build": "next build && next export", "build": "next build && yarn export",
"start": "next start", "start": "next start",
"export": "next export" "export": "next export",
"test:integration": "cypress run",
"argos": "argos upload cypress/screenshots --token $ARGOS_TOKEN --ignore \"**/*(failed*.png\""
}, },
"dependencies": { "dependencies": {
"@fortawesome/fontawesome-free": "^5.15.3", "@fortawesome/fontawesome-free": "^5.15.3",
@ -58,5 +60,11 @@
"reactstrap": "^8.9.0", "reactstrap": "^8.9.0",
"sass": "^1.32.13", "sass": "^1.32.13",
"styled-components": "^5.3.3" "styled-components": "^5.3.3"
},
"devDependencies": {
"argos-cli": "^0.3.3",
"cypress": "^9.4.1",
"http-server": "^14.1.0",
"typescript": "^4.5.5"
} }
} }

@ -1,4 +1,3 @@
import { useState } from "react"; import { useState } from "react";
import Head from "next/head"; import Head from "next/head";
import Link from "next/link"; import Link from "next/link";
@ -6,8 +5,8 @@ import { serverSideTranslations } from "next-i18next/serverSideTranslations";
import { useTranslation } from "next-i18next"; import { useTranslation } from "next-i18next";
import { Container, Row, Col, Button, Input } from "reactstrap"; import { Container, Row, Col, Button, Input } from "reactstrap";
import config from "../next-i18next.config.js"; import config from "../next-i18next.config.js";
import VoteBallot from './vote/[pid]/[[...tid]]'; import VoteBallot from "./vote/[pid]/[[...tid]]";
import CandidatesField from '../components/form/CandidatesField'; import CandidatesField from "../components/form/CandidatesField";
export const getStaticProps = async ({ locale }) => ({ export const getStaticProps = async ({ locale }) => ({
props: { props: {
...(await serverSideTranslations(locale, [], config)), ...(await serverSideTranslations(locale, [], config)),
@ -18,7 +17,6 @@ const Home = () => {
const [title, setTitle] = useState(null); const [title, setTitle] = useState(null);
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
<Container className="homePage"> <Container className="homePage">
<section> <section>
<form className="sectionOneHomeForm" autoComplete="off"> <form className="sectionOneHomeForm" autoComplete="off">
@ -48,17 +46,18 @@ const Home = () => {
value={title ? title : ""} value={title ? title : ""}
onChange={(e) => setTitle(e.target.value)} onChange={(e) => setTitle(e.target.value)}
maxLength="250" maxLength="250"
data-test="question-input"
/> />
</Row> </Row>
<Row> <Row>
<Link href={{ pathname: "/new/", query: { title: title } }}> <Link href={{ pathname: "/new/", query: { title: title } }}>
<Button <Button
type="submit" type="submit"
className="btn btn-block btn-secondary mt-2" className="btn btn-block btn-secondary mt-2"
> >
{t("resource.start")} {t("resource.start")}
<img src="/arrow-white.svg" className="mr-2" /> <img src="/arrow-white.svg" className="mr-2" />
</Button> </Button>
</Link> </Link>
</Row> </Row>
<Row className="noAds"> <Row className="noAds">
@ -67,11 +66,9 @@ const Home = () => {
</Col> </Col>
<Col></Col> <Col></Col>
</Row> </Row>
<Row> <Row></Row>
</Row>
</form> </form>
</section> </section>
<section className="sectionTwoHome"> <section className="sectionTwoHome">
<Row className="sectionTwoRowOne"> <Row className="sectionTwoRowOne">
<Col className="sectionTwoRowOneCol"> <Col className="sectionTwoRowOneCol">
@ -106,39 +103,50 @@ const Home = () => {
</Col> </Col>
</Row> </Row>
<Row className="sectionTwoRowTwo"> <Row className="sectionTwoRowTwo">
<Row className="sectionTwoHomeImage"> <Row className="sectionTwoHomeImage">
<img src="/vote.svg" /> <img src="/vote.svg" />
</Row> </Row>
<Row className="sectionTwoRowTwoCol"> <Row className="sectionTwoRowTwoCol">
<h3 className="col-md-7">Une expérience de vote démocratique et intuitive</h3> <h3 className="col-md-7">
Une expérience de vote démocratique et intuitive
</h3>
</Row> </Row>
<Row className="sectionTwoRowTwoCol"> <Row className="sectionTwoRowTwoCol">
<Col className="sectionTwoRowTwoColText col-md-4"> <Col className="sectionTwoRowTwoColText col-md-4">
<h5 className="">Exprimez toute votre opinion</h5> <h5 className="">Exprimez toute votre opinion</h5>
<p>Au jugement majoritaire, chaque candidat est évalué sur une grille de mention. Vous naurez plus besoin de faire un vote stratégique.</p> <p>
Au jugement majoritaire, chaque candidat est évalué sur une
grille de mention. Vous naurez plus besoin de faire un vote
stratégique.
</p>
</Col> </Col>
<Col className="sectionTwoRowTwoColText col-md-4 offset-md-1"> <Col className="sectionTwoRowTwoColText col-md-4 offset-md-1">
<h5 className="">Obtenez le meilleur consensus</h5> <h5 className="">Obtenez le meilleur consensus</h5>
<p>Le profil des mérites dresse un panorama précis de lopinion des électeurs. Le gagnant du vote est celui qui est la meilleure mention majoritaire.</p> <p>
Le profil des mérites dresse un panorama précis de lopinion des
électeurs. Le gagnant du vote est celui qui est la meilleure
mention majoritaire.
</p>
</Col> </Col>
</Row> </Row>
<Row className="sectionTwoRowThreeCol"> <Row className="sectionTwoRowThreeCol">
<Button <Button className="btn btn-block btn-secondary btn-sectionTwoHome">
className="btn btn-block btn-secondary btn-sectionTwoHome"
>
Découvrez le jugement majoritaire Découvrez le jugement majoritaire
<img src="/arrow-white.svg" className="mr-2" /> <img src="/arrow-white.svg" className="mr-2" />
</Button> </Button>
</Row> </Row>
</Row> </Row>
<Row className="sharing"> <Row className="sharing">
<p>Partagez lapplication Mieux voter</p> <p>Partagez lapplication Mieux voter</p>
<Link href="https://www.facebook.com/mieuxvoter.fr/"><img src="/facebook.svg" className="mr-2" /></Link> <Link href="https://www.facebook.com/mieuxvoter.fr/">
<Link href="https://twitter.com/mieux_voter"><img src="/twitter.svg" className="mr-2" /></Link> <img src="/facebook.svg" className="mr-2" />
</Row> </Link>
<Link href="https://twitter.com/mieux_voter">
<img src="/twitter.svg" className="mr-2" />
</Link>
</Row>
</section> </section>
</Container> </Container>
); );
}; };

13111
yarn.lock

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save