WIP: Refacto & preparation for editorial workflow test #1
16
README.md
16
README.md
|
@ -6,7 +6,21 @@ Pour utiliser :
|
|||
|
||||
```bash
|
||||
npm install
|
||||
npm test
|
||||
|
||||
# lancer les tests authentic sur un environnement local (*.dev.publik.love)
|
||||
PUBLIK_IS_DEV_INSTANCE=true npm run test-devinst -- tests/authentik.feature
|
||||
|
||||
# lancer les tests workflow-editorial sur l'instance de recette de Nîmes
|
||||
## Remplacer les valeurs USERNAME et PASSWORD par le mot de passe à utiliser
|
||||
## Pour les comptes Contributeur et Editeur de cette plateforme
|
||||
export PUBLIK_URL='https://{}-nimes.test.entrouvert.org'
|
||||
export PUBLIK_Contributeur_USERNAME='aberriot'
|
||||
export PUBLIK_Contributeur_PASSWORD='passw0rd'
|
||||
export PUBLIK_Editeur_USERNAME='admin'
|
||||
export PUBLIK_Editeur_PASSWORD='admin'
|
||||
|
||||
npm run test -- features/workflow-editorial.feature
|
||||
|
||||
```
|
||||
|
||||
Cela devrait lancer un navigateur et exécuter les étapes de test implémentées dans `features/*.feature`.
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
module.exports = {
|
||||
default: `--publish-quiet --format-options '{"snippetInterface": "synchronous"}'`
|
||||
default: `--fail-fast --publish-quiet --format-options '{"snippetInterface": "synchronous"}'`
|
||||
}
|
|
@ -1,22 +1,21 @@
|
|||
Fonctionnalité: Authentik
|
||||
Contexte:
|
||||
Sachant que je suis sur la page d'accueil de Combo
|
||||
|
||||
Fonctionnalité: Authentic
|
||||
Scénario: Connexion valide
|
||||
Sachant que je visite la page de connexion
|
||||
Sachant que je suis sur la page "Portail Usager"
|
||||
Et que je clique sur "Connexion"
|
||||
Lorsque je saisis "admin@localhost" dans le champ "username"
|
||||
Et que je saisis "admin" dans le champ "password"
|
||||
Et que je valide le formulaire
|
||||
Alors Je dois être connectée en tant que "admin admin"
|
||||
Alors je dois être connectée en tant que "admin admin"
|
||||
|
||||
Scénario: Déconnexion
|
||||
Lorsque je clique sur "Déconnexion"
|
||||
Alors Je dois ne dois pas être connecté
|
||||
Alors je ne dois pas être connectée
|
||||
|
||||
Scénario: Authentification échouée
|
||||
Sachant que je visite la page de connexion
|
||||
Sachant que je suis sur la page "Portail Usager"
|
||||
Et que je clique sur "Connexion"
|
||||
Lorsque je saisis "admin@localhost" dans le champ "username"
|
||||
Et que je saisis "invalide" dans le champ "password"
|
||||
Et que je valide le formulaire
|
||||
Alors Je dois ne dois pas être connecté
|
||||
Alors je ne dois pas être connectée
|
||||
Et la page doit afficher "Courriel ou mot de passe incorrect"
|
|
@ -1,51 +1,162 @@
|
|||
const assert = require('assert')
|
||||
|
||||
const { Given, When, Then, After, AfterAll, setDefaultTimeout} = require('@cucumber/cucumber');
|
||||
const { Builder, By, Capabilities, Key } = require('selenium-webdriver');
|
||||
const { expect } = require('chai');
|
||||
setDefaultTimeout(5 * 1000);
|
||||
require("chromedriver");
|
||||
const { By, Key, Select } = require('selenium-webdriver');
|
||||
|
||||
// driver setup
|
||||
const capabilities = Capabilities.chrome();
|
||||
capabilities.set('chromeOptions', { "w3c": false });
|
||||
const driver = new Builder().withCapabilities(capabilities).build();
|
||||
const { getDriver } = require('../../src/driver');
|
||||
const { expect } = require('chai');
|
||||
|
||||
// setup
|
||||
setDefaultTimeout(30 * 1000);
|
||||
const driver = getDriver()
|
||||
driver.manage().window().maximize()
|
||||
|
||||
Given("je suis sur la page d'accueil de Combo", async function () {
|
||||
await driver.get('https://combo.dev.publik.love');
|
||||
const PUBLIK_IS_DEV_INSTANCE = (process.env.PUBLIK_IS_DEV_INSTANCE || 'false') === 'true'
|
||||
const PUBLIK_URL = PUBLIK_IS_DEV_INSTANCE ? 'https://{}.dev.publik.love' : (process.env.PUBLIK_URL || 'https://{}.dev.publik.love')
|
||||
const PUBLIK_PORTAIL_USAGER = PUBLIK_IS_DEV_INSTANCE ? 'combo' : process.env.PUBLIK_PORTAIL_USAGER
|
||||
const PUBLIK_PORTAIL_AGENT = PUBLIK_IS_DEV_INSTANCE ? 'agent-combo' : process.env.PUBLIK_PORTAIL_AGENT
|
||||
const PUBLIK_DEMARCHES = PUBLIK_IS_DEV_INSTANCE ? 'wcs' : process.env.PUBLIK_PORTAIL_AGENT
|
||||
const PAGES = {
|
||||
'Portail Usager': PUBLIK_URL.replace('{}', PUBLIK_PORTAIL_USAGER || 'portail'),
|
||||
'Portail Agent': PUBLIK_URL.replace('{}', PUBLIK_PORTAIL_AGENT || 'agents'),
|
||||
'Portail Agent - Fiches': PUBLIK_URL.replace('{}', PUBLIK_DEMARCHES || 'demarches') + '/backoffice/data',
|
||||
}
|
||||
console.log('Available pages:', PAGES)
|
||||
console.log('Launching tests...')
|
||||
|
||||
function sleep(time) {
|
||||
return new Promise(resolve => setTimeout(resolve, time));
|
||||
}
|
||||
|
||||
function getUrlFromPage(pageName) {
|
||||
return PAGES[pageName]
|
||||
}
|
||||
|
||||
async function getElementByText(driver, text) {
|
||||
return await driver.findElement(By.xpath(`//*[contains(text(), "${text}")]`));
|
||||
}
|
||||
|
||||
async function getCurrentUser(driver) {
|
||||
let usernameLink
|
||||
try {
|
||||
usernameLink = await driver.findElement(By.className('connected-user'));
|
||||
} catch (e) {
|
||||
if(e.name === 'NoSuchElementError') {
|
||||
// maybe we're on the agent portal, try with other selector
|
||||
usernameLink = await driver.findElement(By.className('ui-name'));
|
||||
} else {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
let username = await usernameLink.getText()
|
||||
return username
|
||||
}
|
||||
|
||||
async function connect (driver, role) {
|
||||
let homeUrl = getUrlFromPage('Portail Agent')
|
||||
let username = process.env[`PUBLIK_${role}_USERNAME`] || role
|
||||
let password = process.env[`PUBLIK_${role}_PASSWORD`] || 'password'
|
||||
await driver.get(homeUrl)
|
||||
await (await driver.findElement(By.name('username'))).sendKeys(username)
|
||||
await (await driver.findElement(By.name('password'))).sendKeys(password, Key.RETURN)
|
||||
expect(await getCurrentUser(driver)).to.not.be.empty
|
||||
}
|
||||
|
||||
async function disconnect(driver) {
|
||||
let disconnectLink
|
||||
disconnectLink = await driver.findElement(By.className('ui-logout'));
|
||||
disconnectLink = await disconnectLink.findElement(By.xpath('./a'))
|
||||
await disconnectLink.sendKeys(Key.RETURN)
|
||||
await driver.manage().deleteAllCookies()
|
||||
await driver.navigate().refresh()
|
||||
}
|
||||
|
||||
async function getFieldByNameOrLabel(driver, nameOrLabel) {
|
||||
try {
|
||||
// easiest case, there is a field with the same name on the page
|
||||
return await driver.findElement(By.name(nameOrLabel));
|
||||
} catch (e) {
|
||||
if(e.name === 'NoSuchElementError') {
|
||||
// We try to find a <label> tag with the corresponding
|
||||
// content
|
||||
let expression = `//label[text()="${nameOrLabel}"]`
|
||||
let label = await driver.findElement(By.xpath(expression));
|
||||
let fieldId = await label.getAttribute("for")
|
||||
return await driver.findElement(By.id(fieldId));
|
||||
} else {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
Given("je suis sur la page {string}", async function (pageName) {
|
||||
let url = getUrlFromPage(pageName)
|
||||
await driver.get(url);
|
||||
});
|
||||
Given("je visite la page {string}", async function (pageName) {
|
||||
let url = getUrlFromPage(pageName)
|
||||
await driver.get(url);
|
||||
});
|
||||
|
||||
Given('je visite la page de connexion', async function () {
|
||||
let element = await driver.findElement(By.className('login-link'));
|
||||
await element.click();
|
||||
Given("je visite la page {string} sur {string}", async function (path, site) {
|
||||
let siteUrl = getUrlFromPage(site)
|
||||
await driver.get(siteUrl + path);
|
||||
});
|
||||
|
||||
|
||||
When('je saisis {string} dans le champ {string}', async function (text, field) {
|
||||
let element = await driver.findElement(By.name(field));
|
||||
let element = await getFieldByNameOrLabel(driver, field)
|
||||
if (await element.getAttribute("data-godo-schema")) {
|
||||
// this is a godo editor, we need to target the contenteditable
|
||||
// div, not the hidden textarea
|
||||
element = await element.findElement(By.xpath('./following-sibling::*'));
|
||||
element = await element.findElement(By.xpath('.//*[@contenteditable="true"]'));
|
||||
}
|
||||
await element.sendKeys(text)
|
||||
});
|
||||
When('je sélectionne {string} dans le champ {string}', async function (value, field) {
|
||||
let element = await getFieldByNameOrLabel(driver, field)
|
||||
if (await element.getAttribute("class") === 'select2-search__field') {
|
||||
// This is a select2 multiple input with autocomplete, we cannot rely on
|
||||
// built-in browser mechanisms to select a value
|
||||
// so we use the autocomplete feature of the input to type the value
|
||||
// and select it with Enter
|
||||
await input.sendKeys(value, Key.RETURN)
|
||||
} else {
|
||||
let select = new Select(element)
|
||||
await select.selectByVisibleText(value)
|
||||
}
|
||||
});
|
||||
// When('je visite {string}', async function (url) {
|
||||
// if (url.startsWith('/')) {
|
||||
// let currentUrl = new URL(await driver.getCurrentUrl())
|
||||
// url = `${currentUrl.protocol}//${currentUrl.hostname}${url}`
|
||||
// }
|
||||
// return await driver.navigate().to(url)
|
||||
// });
|
||||
|
||||
When('je valide le formulaire', async function () {
|
||||
When('je valide (le formulaire)', async function () {
|
||||
let currentField = await driver.switchTo().activeElement();
|
||||
await currentField.sendKeys(Key.RETURN)
|
||||
});
|
||||
|
||||
|
||||
Then('Je dois être connectée en tant que {string}', async function (expectedUsername) {
|
||||
let usernameLink = await driver.findElement(By.className('connected-user'));
|
||||
let username = await usernameLink.getText()
|
||||
expect(username).to.equal(expectedUsername)
|
||||
Then('je dois être connecté(e) en tant que {string}', async function (expectedUsername) {
|
||||
expect(await getCurrentUser(driver)).to.equal(expectedUsername)
|
||||
});
|
||||
|
||||
When('je clique sur {string}', async function (text) {
|
||||
let element = await driver.findElement(By.xpath(`//*[contains(text(), "${text}")]`));
|
||||
let element = await getElementByText(driver, text)
|
||||
await element.sendKeys(Key.RETURN);
|
||||
});
|
||||
|
||||
When('j\'attends {float} seconde(s)', async function (duration) {
|
||||
await sleep(duration * 1000)
|
||||
});
|
||||
|
||||
Then('Je dois ne dois pas être connecté', async function () {
|
||||
When('je clique sur la ligne {int} du tableau de listing', async function (i) {
|
||||
let link = await driver.findElement(By.xpath(`//table[@id="listing"]//tr[${i}]//a`));
|
||||
await link.click()
|
||||
});
|
||||
|
||||
|
||||
Then('je ne dois pas être connecté(e)', async function () {
|
||||
await driver.findElement(By.className('login-link'));
|
||||
});
|
||||
|
||||
|
@ -53,6 +164,31 @@ Then('la page doit afficher {string}', async function (text) {
|
|||
let element = await driver.findElement(By.xpath(`//*[contains(text(), "${text}")]`));
|
||||
});
|
||||
|
||||
Then('je dois être redirigé vers la page de la fiche', async function () {
|
||||
let currentUrl = new URL(await driver.getCurrentUrl())
|
||||
let path = currentUrl.pathname
|
||||
expect(path.match(/\/backoffice\/data\/fiche\/(\d+)\//)).to.not.be.null
|
||||
});
|
||||
|
||||
Then('la fiche doit être dans le statut {string}', async function (status) {
|
||||
let element = await driver.findElement(By.className('current-status'));
|
||||
let match = (await element.getText()).endsWith(status)
|
||||
expect(match).to.be.true
|
||||
});
|
||||
|
||||
Given("je suis connecté(e) en tant que {string}", async function (role) {
|
||||
await connect(driver, role)
|
||||
});
|
||||
|
||||
Given("je me reconnecte en tant que {string}", async function (role) {
|
||||
await disconnect(driver)
|
||||
await connect(driver, role)
|
||||
});
|
||||
|
||||
After(async function(){
|
||||
await sleep(5000)
|
||||
});
|
||||
|
||||
AfterAll(async function(){
|
||||
await driver.quit();
|
||||
});
|
|
@ -0,0 +1,59 @@
|
|||
Fonctionnalité: Gestion de contenu éditorial
|
||||
|
||||
Scénario: Connexion en tant que contributeur
|
||||
Sachant que je suis connectée en tant que "Contributeur"
|
||||
Et que je suis sur la page "Portail Agent - Fiches"
|
||||
|
||||
Scénario: Ajout d'une fiche
|
||||
Sachant que je clique sur "Fiches pratiques"
|
||||
Et que je clique sur "Ajouter"
|
||||
Quand je saisis "Comment inscrire son enfant à la restauration scolaire" dans le champ "Titre"
|
||||
Et que je sélectionne "Contributeur" dans le champ "Contributeur"
|
||||
Et que je saisis "Pour inscrire son enfant…" dans le champ "Contenu"
|
||||
Et que je sélectionne "Famille" dans le champ "Catégorie(s)"
|
||||
Et que je saisis "lien vers la FAQ" dans le champ "Type du lien 1"
|
||||
# préremplissage
|
||||
Et que je saisis "https://fiche.example/faq" dans le champ "URL"
|
||||
Et que je sélectionne "Quels sont les délais ?" dans le champ "Question 1"
|
||||
Et que je saisis "Les délais sont de 2 semaines en moyenne." dans le champ "Réponse 1"
|
||||
# Si j'envoie le fichier "banner.png" dans le champ ""
|
||||
# Libellé du fichier
|
||||
Et que je clique sur "Valider"
|
||||
Alors je dois être redirigé vers la page de la fiche
|
||||
Et la page doit afficher "Pour inscrire son enfant…"
|
||||
Et la fiche doit être dans le statut "Brouillon"
|
||||
|
||||
Scénario: Modification de fiche
|
||||
Quand je clique sur "Modifier la fiche"
|
||||
Quand je saisis "Pour inscrire son enfant, il faut nous appeler." dans le champ "Contenu"
|
||||
Et que je clique sur "Enregistrer les changements"
|
||||
Alors je dois être redirigé vers la page de la fiche
|
||||
Et la fiche doit être dans le statut "Brouillon"
|
||||
Et la page doit afficher "Pour inscrire son enfant, il faut nous appeler."
|
||||
|
||||
Scénario: Demande de relecture
|
||||
Quand je clique sur "Demander la relecture"
|
||||
Alors je dois être redirigé vers la page de la fiche
|
||||
Et la page doit afficher "En attente de relecture"
|
||||
|
||||
Scénario: Connexion en tant qu'éditeur
|
||||
Sachant que je me reconnecte en tant que "Editeur"
|
||||
Et que je suis sur la page "Portail Agent - Fiches"
|
||||
|
||||
Scénario: Publication
|
||||
Lorsque je clique sur "Fiches pratiques"
|
||||
Et que je clique sur la ligne 1 du tableau de listing
|
||||
Alors la page doit afficher "En attente de relecture"
|
||||
Lorsque je clique sur "Publié"
|
||||
Alors je dois être redirigé vers la page de la fiche
|
||||
Et la page doit afficher "Publié"
|
||||
|
||||
Scénario: Recherche et affichage
|
||||
Sachant que je visite la page "/accueil-avec-recherche-fiche-pour-test" sur "Portail Usager"
|
||||
Et que je saisis "Inscrire" dans le champ "q"
|
||||
Et que j'attends 2 secondes
|
||||
Alors la page doit afficher "Comment inscrire son enfant à la restauration scolaire"
|
||||
Lorsque je clique sur "Comment inscrire son enfant à la restauration scolaire"
|
||||
Et que j'attends 2 secondes
|
||||
Alors la page doit afficher "Lien vers la FAQ"
|
||||
Et la page doit afficher "Quels sont les délais ?"
|
|
@ -4,7 +4,8 @@
|
|||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "cucumber-js --language fr"
|
||||
"test": "cucumber-js --language fr",
|
||||
"test-devinst": "PUBLIK_URL= PUBLIK_IS_DEV_INSTANCE=true cucumber-js --language fr"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
const { Builder, Capabilities } = require('selenium-webdriver');
|
||||
require("chromedriver");
|
||||
|
||||
module.exports.getDriver = function getDriver() {
|
||||
// driver setup
|
||||
const capabilities = Capabilities.chrome();
|
||||
capabilities.set('chromeOptions', { "w3c": false });
|
||||
const driver = new Builder().withCapabilities(capabilities).build();
|
||||
return driver
|
||||
}
|
Loading…
Reference in New Issue