Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ajoute des tests end-to-end pour les audits et rapports #894

Merged
merged 30 commits into from
Dec 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
c6130db
add basic test
hissalht Nov 7, 2024
d2b3023
test audit edition
bellangerq Nov 20, 2024
68001d4
test audit deletion
bellangerq Nov 20, 2024
86171bb
improve seeds generation and document it
bellangerq Nov 20, 2024
709d3c6
add notes test
bellangerq Nov 21, 2024
0c9b44d
add statement test
bellangerq Nov 21, 2024
699b4f0
move tests documentation in cypress folder
bellangerq Nov 21, 2024
ce978f7
add copy audit, criteria search and hide references tests
bellangerq Nov 21, 2024
a85ae38
add tests commands
bellangerq Nov 22, 2024
527185f
make getbylabel accept regex
bellangerq Nov 27, 2024
0a55712
test filters, download, transverse status, finish audit, na on topic …
bellangerq Nov 27, 2024
c63fa72
add some report tests
bellangerq Nov 27, 2024
9e78e5b
seed an audit before each test
bellangerq Nov 28, 2024
e82baa1
seed audits for report tests
bellangerq Nov 28, 2024
d6460bf
use object options in create test audit and fix wait tests
bellangerq Nov 28, 2024
24c1c5c
document tests script and command
bellangerq Nov 28, 2024
2943a62
add todo tests
bellangerq Nov 28, 2024
6a9cb9e
fix regex in test to catch finish date on audit overview page
bellangerq Dec 4, 2024
1d455ea
add account creation tests
bellangerq Dec 4, 2024
22e3dce
handle error where user login with wrong token format
bellangerq Dec 4, 2024
b711943
test user login and settings actions
bellangerq Dec 4, 2024
1fd93ad
temp: to reset
bellangerq Dec 5, 2024
506d6c9
remove unecessary create test account function
bellangerq Dec 12, 2024
404130a
use debug api endpoints to generate test audits
bellangerq Dec 12, 2024
bde173e
test audits list page
bellangerq Dec 12, 2024
d848bbd
update tests doc
bellangerq Dec 12, 2024
6306ee3
lint
bellangerq Dec 12, 2024
a776a71
only import debug controller on certain condition
bellangerq Dec 12, 2024
5591b71
remove faker
bellangerq Dec 12, 2024
c73758b
remove cypress boilerplate files
bellangerq Dec 12, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion confiture-rest-api/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { MailModule } from "./mail/mail.module";
import { AuthModule } from "./auth/auth.module";
import { ProfileModule } from "./profile/profile.module";
import { UserMiddleware } from "./auth/user.middleware";
import { DebugController } from "./debug.controller";
import { PrismaService } from "./prisma.service";

@Module({
imports: [
Expand All @@ -21,7 +23,12 @@ import { UserMiddleware } from "./auth/user.middleware";
AuthModule,
ProfileModule
],
controllers: [HealthCheckController]
providers: process.env.DEBUG_ENDPOINTS ? [PrismaService] : [],
controllers: [
HealthCheckController,
// enable debug enpoints only when the DEBUG_ENDPOINTS variable is set
...(process.env.DEBUG_ENDPOINTS ? [DebugController] : [])
]
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
Expand Down
3 changes: 2 additions & 1 deletion confiture-rest-api/src/auth/auth.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { CreateAccountController } from "./create-account.controller";
}),
FeedbackModule,
AuditsModule
]
],
exports: [AuthService]
})
export class AuthModule {}
199 changes: 199 additions & 0 deletions confiture-rest-api/src/debug.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
import { Body, Controller, Post } from "@nestjs/common";
import { PrismaService } from "./prisma.service";
import { AuthService } from "./auth/auth.service";
import {
CriterionResultStatus,
CriterionResultUserImpact
} from "@prisma/client";
import { nanoid } from "nanoid";
import { CRITERIA } from "./audits/criteria";

@Controller("debug")
export class DebugController {
constructor(
private readonly prisma: PrismaService,
private readonly auth: AuthService
) {}

@Post("verification-token")
async getAccountVerificationToken(@Body() body: { username: string }) {
const token = this.auth.regenerateVerificationToken(body.username);
return token;
}

@Post("password-reset-verification-token")
async getPasswordResetVerificationToken(@Body() body: { username: string }) {
const token = this.auth.generatePasswordResetVerificationToken(
body.username
);

return token;
}

@Post("email-update-verification-token")
async getEmailUpdateVerificationToken(@Body() body: { uid: string }) {
const token = this.auth.regenerateEmailUpdateVerificationToken(body.uid);

return token;
}

@Post("create-verified-user")
async createVerifiedUser() {
const email = `john-doe${Math.random()}@example.com`;
const password = "pouetpouetpouet";

await this.auth.createUnverifiedUser(email, password);
const user = await this.prisma.user.update({
data: {
isVerified: true,
verificationJti: null
},
where: {
username: email
}
});

const authToken = await this.auth.signin(email, password);

return {
username: email,
password,
authToken,
uid: user.uid
};
}

@Post("create-audit")
async createAudit(
@Body()
body: {
isComplete: boolean;
isPristine: boolean;
noImprovements: boolean;
auditorEmail?: string;
}
) {
const editUniqueId = `edit-${nanoid()}`;
const reportUniqueId = `report-${nanoid()}`;

const completedAudit = await this.prisma.audit.create({
data: {
editUniqueId: editUniqueId,
consultUniqueId: reportUniqueId,
creationDate: new Date(),
publicationDate: body.isComplete ? new Date() : null,
auditTrace: {
create: {
auditConsultUniqueId: editUniqueId,
auditEditUniqueId: reportUniqueId
}
},
auditType: "FULL",
procedureName: "Audit de mon petit site",
auditorEmail: body.auditorEmail || "[email protected]",
auditorName: "Étienne Durand",
transverseElementsPage: {
create: {
name: "Éléments transverses",
url: ""
}
},
pages: {
createMany: {
data: [
{
name: "Accueil",
url: "https://example.com"
},
{
name: "Contact",
url: "https://example.com/contact"
},
{
name: "À propos",
url: "https://example.com/a-propos"
},
{
name: "Blog",
url: "https://example.com/blog"
},
{
name: "Article",
url: "https://example.com/blog/article"
},
{
name: "Connexion",
url: "https://example.com/connexion"
},
{
name: "Documentation",
url: "https://example.com/documentation"
},
{
name: "FAQ",
url: "https://example.com/faq"
}
]
}
}
},
include: {
transverseElementsPage: true
}
});

const auditPages = await this.prisma.auditedPage.findMany({
where: {
auditUniqueId: editUniqueId
}
});

if (!body.isPristine) {
await Promise.all(
[completedAudit.transverseElementsPage, ...auditPages].map(async (p) =>
this.prisma.criterionResult.createMany({
data: CRITERIA.map((c, i) => ({
status: [
CriterionResultStatus.COMPLIANT,
CriterionResultStatus.NOT_APPLICABLE,
CriterionResultStatus.NOT_COMPLIANT
][i % 3],
notCompliantComment: "Une erreur ici",
notApplicableComment: body.noImprovements
? null
: "Attention quand même si ça devient applicable",
compliantComment: body.noImprovements ? null : "Peut mieux faire",
quickWin: i % 7 === 0,
userImpact: [
CriterionResultUserImpact.MINOR,
CriterionResultUserImpact.MAJOR,
CriterionResultUserImpact.BLOCKING,
null
][i % 4],
topic: c.topic,
criterium: c.criterium,
pageId: p.id
}))
})
)
);
}

if (!body.isComplete && !body.isPristine) {
await this.prisma.criterionResult.delete({
where: {
pageId_topic_criterium: {
topic: 1,
criterium: 1,
pageId: auditPages[0].id
}
}
});
}

return {
editId: editUniqueId,
reportId: reportUniqueId
};
}
}
18 changes: 0 additions & 18 deletions confiture-web-app/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,24 +31,6 @@ Lancer le [serveur local sur le port 3000](http://localhost:3000) :
yarn dev
```

## Tests

[Cypress](https://www.cypress.io/) est utilisé pour lancer des tests end-to-end (e2e) dans un navigateur pour reproduire le comportement des utilisateurs.

Les tests peuvent être lancés de 2 manières :

- Via l’application Cypress avec :

```sh
yarn cypress open
```

- Via le terminal avec :

```sh
yarn cypress run
```

## Guidelines

- Utiliser les media queries en "desktop first" et avec la notation suivante avec les [valeurs de points de rupture du DSFR](https://www.systeme-de-design.gouv.fr/elements-d-interface/fondamentaux-techniques/grille-et-points-de-rupture) :
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ function copyAuditLink(uniqueId: string) {
}

function copyReportLink(uniqueId: string) {
const url = `${window.location.origin}/rapports/${uniqueId}`;
const url = `${window.location.origin}/rapport/${uniqueId}`;

navigator.clipboard.writeText(url).then(() => {
notify(
Expand Down Expand Up @@ -174,8 +174,8 @@ function copyStatementLink(uniqueId: string) {
'fr-badge--purple-glycine': isInProgress || isNotStarted
}"
>
<span class="fr-sr-only">Statut </span>
{{ isInProgress || isNotStarted ? "En cours" : "Terminé" }}
<span class="fr-sr-only">Statut </span
>{{ isInProgress || isNotStarted ? "En cours" : "Terminé" }}
</p>

<!-- Creation date -->
Expand Down Expand Up @@ -273,7 +273,7 @@ function copyStatementLink(uniqueId: string) {
? "Continuer l’audit"
: "Voir le rapport"
}}
<span v-if="isInProgress" class="fr-sr-only">
<span v-if="isInProgress || isNotStarted" class="fr-sr-only">
{{ audit.procedureName }}</span
>
<span v-else class="fr-sr-only"
Expand Down
3 changes: 1 addition & 2 deletions confiture-web-app/src/components/audit/AraTabs.vue
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,7 @@ watch(currentTab, (currentTab) => {
@keydown.home.prevent="selectFirstTab"
@keydown.end.prevent="selectLastTab"
>
<LayoutIcon v-if="i === 0" class="fr-mr-2v" />
{{ tab.label }}
<LayoutIcon v-if="i === 0" class="fr-mr-2v" />{{ tab.label }}
</button>
</li>
</ul>
Expand Down
1 change: 0 additions & 1 deletion confiture-web-app/src/components/audit/PagesSample.vue
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,6 @@ function updatePageOrder(startIndex: number, endIndex: number) {
class="fr-btn fr-btn--tertiary-no-outline"
type="button"
:disabled="pages.length === 1"
data-cy="delete"
@click="deletePage(i)"
>
Supprimer
Expand Down
4 changes: 3 additions & 1 deletion confiture-web-app/src/pages/report/ReportPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ const tabs = computed(() => [
const showCopyAlert = ref(false);

async function copyReportUrl() {
const url = `${window.location.origin}/rapports/${uniqueId}`;
const url =
window.location.origin +
router.resolve({ name: "report", params: { uniqueId } }).fullPath;

navigator.clipboard.writeText(url).then(() => {
showCopyAlert.value = true;
Expand Down
11 changes: 7 additions & 4 deletions confiture-web-app/src/store/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,14 @@ export const useAccountStore = defineStore("account", {
let authToken = localStorage.getItem(AUTH_TOKEN_STORAGE_KEY) ?? null;

if (authToken) {
const payload = jwtDecode(authToken) as AuthenticationJwtPayload;
try {
const payload = jwtDecode(authToken) as AuthenticationJwtPayload;

const isTokenExpired = payload.exp * 1000 < Date.now();

if (isTokenExpired) {
const isTokenExpired = payload.exp * 1000 < Date.now();
if (isTokenExpired) {
throw "tokenIsExpired";
}
} catch {
authToken = null;
localStorage.removeItem(AUTH_TOKEN_STORAGE_KEY);
}
Expand Down
1 change: 1 addition & 0 deletions cypress/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
downloads/
38 changes: 38 additions & 0 deletions cypress/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Tests end-to-end

[Cypress](https://www.cypress.io/) est utilisé pour lancer des tests end-to-end (e2e) dans un navigateur pour reproduire le comportement des utilisateurs.

En parallèle des tests, le serveur back-end doit être lancé avec la variable `DEBUG_ENDPOINTS` à `1` :

```sh
DEBUG_ENDPOINTS=1 yarn start:dev
```

Différentes données (audit, rapport ou compte) sont créées avant chaque test et de manière indépendante. Il est possible d’ajouter des options en fonction de la donnée souhaitée avec les fonctions [`createTestAccount` et `createTestAudit()`](/cypress/support/commands.ts).

Les tests peuvent être lancés de 2 manières, depuis la racine du projet :

- Via l’application Cypress avec :

```sh
yarn tests:open
```

- Via le terminal avec :

```sh
yarn tests:run
```

## Guidelines

- Cypress ne supporte pas la gestion des liens externes avec `target="_blank"`. Dans ce cas, il faut ajouter `.invoke("removeAttr", "target")` sur le lien avant de cliquer dessus. Exemple :

```js
cy.contains("a", "Compléter").invoke("removeAttr", "target").click();
```

- Il est préférable d'indique le type d’élément ciblé avec `contains()` pour s’assurer qu’il ne s’agisse pas d’un autre texte sur la page. Exemple :
```js
cy.contains("button", "Valider les paramètres").click();
```
Loading
Loading