⚙️ 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
steps:
- checkout
- restore_cache:
name: Restore Yarn Package Cache
keys:
- v1-node_modules-{{ checksum "yarn.lock" }}
- run:
name: update-yarn
command: 'curl --compressed -o- -L https://yarnpkg.com/install.sh | bash'
name: Install Dependencies
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:
name: install-dependencies
command: yarn install
name: build app
command: yarn build
- run:
name: compile-translations
command: yarn translate
name: integration tests
command: npx http-server build --port 3000 --silent & yarn test:integration
- run:
name: test
command: yarn test
name: Store failing screenshots
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
functions/next_*
.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,
"scripts": {
"dev": "next dev",
"build": "next build && next export",
"build": "next build && yarn export",
"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": {
"@fortawesome/fontawesome-free": "^5.15.3",
@ -58,5 +60,11 @@
"reactstrap": "^8.9.0",
"sass": "^1.32.13",
"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 Head from "next/head";
import Link from "next/link";
@ -6,8 +5,8 @@ import { serverSideTranslations } from "next-i18next/serverSideTranslations";
import { useTranslation } from "next-i18next";
import { Container, Row, Col, Button, Input } from "reactstrap";
import config from "../next-i18next.config.js";
import VoteBallot from './vote/[pid]/[[...tid]]';
import CandidatesField from '../components/form/CandidatesField';
import VoteBallot from "./vote/[pid]/[[...tid]]";
import CandidatesField from "../components/form/CandidatesField";
export const getStaticProps = async ({ locale }) => ({
props: {
...(await serverSideTranslations(locale, [], config)),
@ -18,7 +17,6 @@ const Home = () => {
const [title, setTitle] = useState(null);
const { t } = useTranslation();
return (
<Container className="homePage">
<section>
<form className="sectionOneHomeForm" autoComplete="off">
@ -48,17 +46,18 @@ const Home = () => {
value={title ? title : ""}
onChange={(e) => setTitle(e.target.value)}
maxLength="250"
data-test="question-input"
/>
</Row>
<Row>
<Link href={{ pathname: "/new/", query: { title: title } }}>
<Button
type="submit"
className="btn btn-block btn-secondary mt-2"
>
{t("resource.start")}
<img src="/arrow-white.svg" className="mr-2" />
</Button>
<Link href={{ pathname: "/new/", query: { title: title } }}>
<Button
type="submit"
className="btn btn-block btn-secondary mt-2"
>
{t("resource.start")}
<img src="/arrow-white.svg" className="mr-2" />
</Button>
</Link>
</Row>
<Row className="noAds">
@ -67,11 +66,9 @@ const Home = () => {
</Col>
<Col></Col>
</Row>
<Row>
</Row>
<Row></Row>
</form>
</section>
</section>
<section className="sectionTwoHome">
<Row className="sectionTwoRowOne">
<Col className="sectionTwoRowOneCol">
@ -106,39 +103,50 @@ const Home = () => {
</Col>
</Row>
<Row className="sectionTwoRowTwo">
<Row className="sectionTwoHomeImage">
<Row className="sectionTwoHomeImage">
<img src="/vote.svg" />
</Row>
</Row>
<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 className="sectionTwoRowTwoCol">
<Col className="sectionTwoRowTwoColText col-md-4">
<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 className="sectionTwoRowTwoColText col-md-4 offset-md-1">
<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>
</Row>
<Row className="sectionTwoRowThreeCol">
<Button
className="btn btn-block btn-secondary btn-sectionTwoHome"
>
<Button className="btn btn-block btn-secondary btn-sectionTwoHome">
Découvrez le jugement majoritaire
<img src="/arrow-white.svg" className="mr-2" />
<img src="/arrow-white.svg" className="mr-2" />
</Button>
</Row>
</Row>
<Row className="sharing">
<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://twitter.com/mieux_voter"><img src="/twitter.svg" className="mr-2" /></Link>
</Row>
<Link href="https://www.facebook.com/mieuxvoter.fr/">
<img src="/facebook.svg" className="mr-2" />
</Link>
<Link href="https://twitter.com/mieux_voter">
<img src="/twitter.svg" className="mr-2" />
</Link>
</Row>
</section>
</Container>
);
};

13111
yarn.lock

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