Skip to content
Snippets Groups Projects
Commit 3a248d48 authored by ESTELLON Bertrand's avatar ESTELLON Bertrand
Browse files

Initial commit

parents
No related branches found
No related tags found
No related merge requests found
Showing with 608 additions and 0 deletions
logs
*.log
npm-debug.log*
pids
*.pid
*.seed
lib-cov
coverage
.nyc_output
.grunt
.lock-wscript
node_modules
jspm_packages
.npm
.node_repl_history
profile-*
.DS_Store
*.swp
.idea
.tap/
.vscode
*code-workspace
profile*
*clinic*
*flamegraph*
yarn.lock
package-lock.json
\ No newline at end of file
Source diff could not be displayed: it is too large. Options to address this: view the blob.
Hello world !
\ No newline at end of file
const progression = process.env.PROGRESSION || 0;
import { expect, test } from 'vitest'
import { Game } from './game.js';
import { ServerApp } from './server-app.js';
import { JSDOM } from 'jsdom'
import fs from 'fs';
import puppeteer from 'puppeteer';
export const data = {
"levels": [
{"id": 1, "length": 5, "theme": "Littérature", "difficulty": 3},
{"id": 2, "length": 6, "theme": "Sciences", "difficulty": 4},
{"id": 3, "length": 7, "theme": "Art", "difficulty": 5}
],
"words": {
"1": "LIVRE",
"2": "SAVANT",
"3": "PEINTRE"
}
};
test('TD 2 [00] : tests', () => {
expect(true).toStrictEqual(true);
});
if (progression == 1) {
test('TD 2 [01] : classes', () => {
const game = new Game();
expect(game).toBeInstanceOf(Game);
});
}
if (progression >= 2) {
test('TD 2 [02] : constructeurs', () => {
expect(Game.prototype.constructor.name).toStrictEqual("Game");
expect(Game.prototype.constructor.length).toStrictEqual(1);
expect(Game.prototype.constructor.toString().replaceAll(' ', '')).toContain("constructor(data)");
});
}
if (progression >= 3) {
test('TD 2 [03] : propriétés', () => {
const game = new Game("test");
expect(game.data).toStrictEqual("test");
});
}
if (progression >= 4) {
test('TD 2 [04] : méthodes', () => {
const game = new Game({levels: "test"});
expect(game.levels()).toStrictEqual("test");
});
}
if (progression >= 5) {
test('TD 2 [05] : boucle for et conditions', () => {
const game = new Game(data);
expect(game.level(2)).toStrictEqual(data.levels[1]);
if (progression == 5) { expect(game.level(20)).toStrictEqual(undefined); }
});
}
if (progression >= 6) {
test('TD 2 [06] : exceptions', () => {
const game = new Game(data);
expect(game.level(2)).toStrictEqual(data.levels[1]);
expect(() => game.level(20)).toThrow('Niveau non trouvé');
});
}
if (progression >= 7) {
test('TD 2 [07] : undefined', () => {
const game = new Game(data);
expect(game.word(2)).toStrictEqual(data.words[2]);
expect(() => game.word(20)).toThrow('Mot non trouvé');
});
}
if (progression >= 8) {
test('TD 2 [08] : chaînes de caractères, variables et tableaux', () => {
const game = new Game(data);
expect(game.stringToArray("test")).toStrictEqual(['T', 'E', 'S', 'T']);
});
}
if (progression >= 9) {
test('TD 2 [09] : tri', () => {
const game = new Game(data);
expect(game.letters(2)).toStrictEqual(['A', 'A', 'N', 'S', 'T', 'V']);
});
}
if (progression >= 10) {
test('TD 2 [10] : objets', () => {
const game = new Game(data);
expect(game.computeLine(2, "SAVANT")).toStrictEqual([
{letter: 'S', state: true},
{letter: 'A', state: true},
{letter: 'V', state: true},
{letter: 'A', state: true},
{letter: 'N', state: true},
{letter: 'T', state: true}
]);
expect(game.computeLine(2, "STVB")).toStrictEqual([
{letter: 'S', state: true},
{letter: 'T', state: false},
{letter: 'V', state: true},
{letter: 'B', state: false},
{letter: '_', state: false},
{letter: '_', state: false}
]);
expect(game.computeLine(2, "SAVANTT")).toStrictEqual([
{letter: 'S', state: true},
{letter: 'A', state: true},
{letter: 'V', state: true},
{letter: 'A', state: true},
{letter: 'N', state: true},
{letter: 'T', state: true}
]);
});
}
if (progression >= 11) {
test('TD 2 [11] : mise en place du serveur web', () => {
const game = new Game(data);
const app = new ServerApp(game);
expect(app.start).toBeInstanceOf(Function);
expect(app.build).toBeInstanceOf(Function);
app.start(3000, true);
});
}
if (progression >= 12) {
test('TD 2 [12] : documents statiques', async () => {
const game = new Game(data);
const app = new ServerApp(game);
const fastify = app.build(false);
const response = await fastify.inject({url: '/favicon.svg'});
expect(response.statusCode).toStrictEqual(200);
expect(response.headers['content-type']).toStrictEqual('image/svg+xml');
const content = fs.readFileSync(new URL('static/favicon.svg', import.meta.url), 'utf8');
expect(response.body).toStrictEqual(content);
});
}
if (progression >= 13) {
test('TD 2 [13] : mise en place de la première route', async () => {
const game = new Game(data);
const app = new ServerApp(game);
const fastify = app.build(false);
const response = await fastify.inject({url: '/'});
expect(response.statusCode).toStrictEqual(200);
if (progression == 13) {
expect(response.headers['content-type']).contain('text/html');
expect(response.body).toStrictEqual('<h1>Hello World !</h1>');
}
});
}
if (progression >= 14 && progression < 16) {
test('TD 2 [14] : envoi de la liste des niveaux en JSON', async () => {
const game = new Game(data);
const app = new ServerApp(game);
const fastify = app.build(false);
const response = await fastify.inject({url: '/'});
expect(response.statusCode).toStrictEqual(200);
expect(response.headers['content-type']).contain('application/json');
expect(response.json()).toStrictEqual(data.levels);
});
}
if (progression == 15) {
test('TD 2 [15] : deuxième route en JSON', async () => {
const game = new Game(data);
const app = new ServerApp(game);
const fastify = app.build(false);
const response = await fastify.inject({url: '/level/2'});
expect(response.statusCode).toStrictEqual(200);
expect(response.headers['content-type']).contain('application/json');
expect(response.json()).toStrictEqual({
level: data.levels[1],
letters: ['A', 'A', 'N', 'S', 'T', 'V']
});
const response2 = await fastify.inject({url: '/level/20'});
expect(response2.statusCode).toStrictEqual(500);
});
}
if (progression >= 16) {
test('TD 2 [16] : mise en place des templates', async () => {
const game = new Game(data);
const app = new ServerApp(game);
const fastify = app.build(false);
const response = await fastify.inject({url: '/'});
expect(response.statusCode).toStrictEqual(200);
expect(response.headers['content-type']).contain('text/html');
expect(response.body).toContain('<th>Difficulté</th>');
const response2 = await fastify.inject({url: '/level/2'});
expect(response2.statusCode).toStrictEqual(200);
expect(response2.headers['content-type']).contain('text/html');
expect(response2.body).toContain('lettres)');
});
}
if (progression >= 17) {
test('TD 2 [17] : template index.ejs', async () => {
const game = new Game(data);
const app = new ServerApp(game);
const fastify = app.build(false);
const response = await fastify.inject({ url: '/' });
const dom = new JSDOM(response.payload);
let html = dom.window.document.querySelector('tbody').innerHTML;
html = html.trim().split('\n').map(line=>line.trim()).join('');
expect(html).toStrictEqual('<tr><td>5 lettres</td><td>Littérature</td><td>★★★</td><td><a href=\"level/1\">Démarrer</a></td></tr>'+
'<tr><td>6 lettres</td><td>Sciences</td><td>★★★★</td><td><a href=\"level/2\">Démarrer</a></td></tr>'+
'<tr><td>7 lettres</td><td>Art</td><td>★★★★★</td><td><a href=\"level/3\">Démarrer</a></td></tr>');
});
}
if (progression >= 18) {
test('TD 2 [18] : template level.ejs', async () => {
const game = new Game(data);
const app = new ServerApp(game);
const fastify = app.build(false);
const response = await fastify.inject({ url: '/level/1' });
const dom = new JSDOM(response.payload);
let trElements = dom.window.document.querySelectorAll('tr');
expect(trElements.length).toBeGreaterThanOrEqual(1);
const firstTrElement = trElements[0];
let html = firstTrElement.innerHTML.trim().split('\n').map(line=>line.trim()).join('');
expect(html).toStrictEqual('<td class="text-bg-primary">E</td><td class="text-bg-primary">I</td><td class="text-bg-primary">L</td>'+
'<td class="text-bg-primary">R</td><td class="text-bg-primary">V</td>');
});
}
if (progression >= 19) {
test('TD 2 [19] : gestion des erreurs 500', async () => {
const game = new Game(data);
const app = new ServerApp(game);
const fastify = app.build(false);
fastify.get("/error", (request, reply) => { throw new Error("Error message"); });
const response = await fastify.inject({ url: '/error' });
expect(response.statusCode).toStrictEqual(500);
expect(response.payload).contain('<p class="lead">Error message</p>');
});
}
if (progression >= 20) {
test('TD 2 [20] : gestion des erreurs 404', async () => {
const game = new Game(data);
const app = new ServerApp(game);
const fastify = app.build(false);
const response = await fastify.inject({ url: '/unknown' });
expect(response.statusCode).toStrictEqual(404);
expect(response.payload).contain('<p class="lead">Impossible de trouver cette page</p>');
});
}
if (progression >= 21) {
test('TD 3 [21] : mise en place d\'un formulaire', async () => {
const game = new Game(data);
const app = new ServerApp(game);
const fastify = app.build(false);
const response = await fastify.inject({ url: '/level/1' });
const dom = new JSDOM(response.payload);
const form = dom.window.document.querySelector('form');
expect(form).not.toBeNull();
expect(form.method).toStrictEqual('post');
expect(form.action).toStrictEqual('/level/1');
const input = form.querySelector('input');
expect(input).not.toBeNull();
expect(input.type).toStrictEqual('text');
expect(input.name).toStrictEqual('word');
const button = form.querySelector('button');
expect(button).not.toBeNull();
expect(button.type).toStrictEqual('submit');
expect(button.textContent).toStrictEqual('Proposer');
});
}
if (progression == 22) {
test('TD 3 [22] : traitement de la requête POST', async () => {
const game = new Game(data);
const app = new ServerApp(game);
const fastify = app.build(true);
const response = await fastify.inject({ url: '/level/1', method: 'POST', payload: {word: 'LIVES'} });
expect(response.statusCode).toStrictEqual(200);
expect(response.json()).toStrictEqual([
{letter: 'L', state: true},
{letter: 'I', state: true},
{letter: 'V', state: true},
{letter: 'E', state: false},
{letter: 'S', state: false}
]);
});
}
if (progression == 23) {
test('TD 3 [23] : affichage des lignes', async () => {
const game = new Game(data);
const app = new ServerApp(game);
const fastify = app.build(false);
const response = await fastify.inject({ url: '/level/1', method: 'POST', payload: {word: 'LIVES'} });
const dom = new JSDOM(response.payload);
const trElements1 = dom.window.document.querySelectorAll('tr');
expect(trElements1.length).toStrictEqual(2);
const lastTrElements1 = trElements1[trElements1.length-1];
const html = lastTrElements1.innerHTML.trim().split('\n').map(line=>line.trim()).join('');
expect(html).toStrictEqual('<td class="text-bg-success">L</td><td class="text-bg-success">I</td><td class="text-bg-success">V</td>'+
'<td class="text-bg-danger">E</td><td class="text-bg-danger">S</td>');
const response2 = await fastify.inject({ url: '/level/1', method: 'GET' });
const dom2 = new JSDOM(response2.payload);
const trElements2 = dom2.window.document.querySelectorAll('tr');
expect(trElements2.length).toStrictEqual(1);
const lastTrElements2 = trElements2[trElements2.length-1];
const html2 = lastTrElements2.innerHTML.trim().split('\n').map(line=>line.trim()).join('');
expect(html2).toStrictEqual('<td class="text-bg-primary">E</td><td class="text-bg-primary">I</td><td class="text-bg-primary">L</td>'+
'<td class="text-bg-primary">R</td><td class="text-bg-primary">V</td>');
const templateHTML = await fastify.view('templates/level.ejs', {
level: data.levels[0],
letters: game.letters(1),
lines: [game.computeLine(1, 'LIVES'), game.computeLine(1, 'LIVRE')]
});
const dom3 = new JSDOM(templateHTML);
const trElements3 = dom3.window.document.querySelectorAll('tr');
expect(trElements3.length).toStrictEqual(3);
const trElement31 = trElements3[1];
const trElement32 = trElements3[2];
const html31 = trElement31.innerHTML.trim().split('\n').map(line=>line.trim()).join('');
expect(html31).toStrictEqual('<td class="text-bg-success">L</td><td class="text-bg-success">I</td><td class="text-bg-success">V</td>'+
'<td class="text-bg-danger">E</td><td class="text-bg-danger">S</td>');
const html32 = trElement32.innerHTML.trim().split('\n').map(line=>line.trim()).join('');
expect(html32).toStrictEqual('<td class="text-bg-success">L</td><td class="text-bg-success">I</td><td class="text-bg-success">V</td>'+
'<td class="text-bg-success">R</td><td class="text-bg-success">E</td>');
});
}
if (progression >= 24) {
test('TD 3 [24] : utilisation des sessions', async () => {
const game = new Game(data);
const app = new ServerApp(game);
const fastify = app.build(false);
await fastify.listen();
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto(`http://localhost:${fastify.server.address().port}/level/1`);
await page.waitForSelector('input');
await page.type('input', 'LIVES');
await page.click('button');
await page.waitForSelector('input');
await page.type('input', 'LIVRE');
await page.click('button');
await page.waitForSelector('input');
const html = await page.content();
const dom = new JSDOM(html);
let tbody = dom.window.document.querySelector('tbody').innerHTML
tbody = tbody.trim().split('\n').map(line=>line.trim()).join('');
const expectedHTML = '<tr><td class="text-bg-primary">E</td><td class="text-bg-primary">I</td><td class="text-bg-primary">L</td>'+
'<td class="text-bg-primary">R</td><td class="text-bg-primary">V</td></tr>'+
'<tr><td class="text-bg-success">L</td><td class="text-bg-success">I</td><td class="text-bg-success">V</td>'+
'<td class="text-bg-danger">E</td><td class="text-bg-danger">S</td></tr>'+
'<tr><td class="text-bg-success">L</td><td class="text-bg-success">I</td><td class="text-bg-success">V</td>'+
'<td class="text-bg-success">R</td><td class="text-bg-success">E</td></tr>';
expect(tbody).toStrictEqual(expectedHTML);
await browser.close();
await fastify.close();
});
}
if (progression >= 25) {
test('TD 3 [25] : redirections', async () => {
const game = new Game(data);
const app = new ServerApp(game);
const fastify = app.build(false);
const response = await fastify.inject({
url: '/level/1',
method: 'POST',
payload: {word: 'LIVRE'}
});
expect(response.statusCode).toStrictEqual(302);
expect(response.headers.location).toStrictEqual('/level/1');
});
}
if (progression >= 26) {
test('TD 4 [>=26] : Vous n\'êtes pas dans le bon répertoire !', async () => {
expect(true).toStrictEqual(false);
});
}
\ No newline at end of file
export const data = {
"levels": [
{"id": 1, "length": 5, "theme": "Littérature", "difficulty": 3},
{"id": 2, "length": 6, "theme": "Sciences", "difficulty": 4},
{"id": 3, "length": 6, "theme": "Culture générale", "difficulty": 5},
{"id": 4, "length": 7, "theme": "Géographie", "difficulty": 5},
{"id": 5, "length": 6, "theme": "Fruits", "difficulty": 3},
{"id": 6, "length": 9, "theme": "Pâtisserie", "difficulty": 4},
{"id": 7, "length": 5, "theme": "Sports", "difficulty": 5},
{"id": 8, "length": 8, "theme": "Nature", "difficulty": 4}
],
"words": {
"1": "LIVRE",
"2": "SAVANT",
"3": "AFFLUX",
"4": "CASCADE",
"5": "CERISE",
"6": "CROISSANT",
"7": "KAYAK",
"8": "MONTAGNE"
}
};
\ No newline at end of file
{
"type": "module",
"scripts": {
"test": "PROGRESSION=\"${npm_config_progression}\" vitest run all.test.js"
}
}
import Fastify from "fastify";
import { fastifyView } from "@fastify/view";
import { fastifyStatic } from "@fastify/static";
import { fastifyFormbody } from "@fastify/formbody";
import { fastifyCookie } from "@fastify/cookie";
import { fastifySession } from "@fastify/session";
import ejs from "ejs";
export class ServerApp {
constructor(game) {
this.game = game;
}
}
\ No newline at end of file
Source diff could not be displayed: it is too large. Options to address this: view the blob.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg"
width="20" height="20" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24">
<rect x="0" y="0" width="100%" height="100%" fill="black" />
<path stroke="lightblue" fill="none" d="M 6.1,6.6 8.4,2.1 14.1,1.9 17.9,5.7 16.1,9.4 11.7,12.6 11.2,17.2" />
<circle stroke="red" cx="11.2" cy="21.6" r="1.2" />
</svg>
\ No newline at end of file
<!doctype html>
<html>
<head>
<title>La revanche des lettres</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN"
crossorigin="anonymous">
<link rel="icon" href="/favicon.svg" />
</head>
<body>
<div class="navbar navbar-dark bg-dark">
<div class="container">
<a href="/" class="navbar-brand">
<img src="/favicon.svg" alt="favicon">
<strong>La revanche des lettres</strong>
</a>
</div>
</div>
<div class="container">
<div class="d-flex align-items-center justify-content-center vh-100">
<div class="text-center">
<h1 class="display-1 fw-bold"><%= code %></h1>
<p class="lead"><%= error %></p>
<a href="/" class="btn btn-primary">Retour à la liste des niveaux</a>
</div>
</div>
</div>
</body>
</html>
\ No newline at end of file
<!doctype html>
<html>
<head>
<title>La revanche des lettres</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN"
crossorigin="anonymous">
<link rel="icon" href="/favicon.svg" />
</head>
<body>
<div class="navbar navbar-dark bg-dark">
<div class="container">
<a href="/" class="navbar-brand">
<img src="/favicon.svg" alt="favicon">
<strong>La revanche des lettres</strong>
</a>
</div>
</div>
<div class="container">
<br>
<table class="table table-striped table-hover">
<thead>
<tr>
<th>Longueur</th>
<th>Thème</th>
<th>Difficulté</th>
<th></th>
</tr>
</thead>
<tbody>
<!-- TODO : générer la liste des niveaux -->
</tbody>
</table>
</div>
</body>
</html>
\ No newline at end of file
<!doctype html>
<html>
<head>
<title>La revanche des lettres</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN"
crossorigin="anonymous">
<link rel="icon" href="/favicon.svg" />
</head>
<body>
<div class="navbar navbar-dark bg-dark">
<div class="container">
<a href="/" class="navbar-brand">
<img src="/favicon.svg" alt="favicon">
<strong>La revanche des lettres</strong>
</a>
<span class="navbar-text"><!-- TODO 2 --> (<!-- TODO 3--> lettres)</span>
</div>
</div>
<div class="container">
<br>
<div class="col-4 offset-4">
<table class="table table-bordered text-center">
<tbody>
<!-- TODO 1 -->
<!-- TODO 5 -->
</tbody>
</table>
<!-- TODO 4 -->
</div>
</div>
</body>
</html>
\ No newline at end of file
This diff is collapsed.
import { ServerApp } from "./server-app.js";
const app = new ServerApp();
app.start(5000, true);
\ No newline at end of file
{
"type": "module",
"scripts": {
"test": "PROGRESSION=\"${npm_config_progression}\" vitest run all.test.js"
}
}
import Fastify from "fastify";
import fs from "fs";
import { fastifyStatic } from "@fastify/static";
export class ServerApp {
build(logEnabled) {
const fastify = new Fastify({logger: logEnabled})
fastify.register(fastifyStatic, { root: new URL('static', import.meta.url) });
fastify.setNotFoundHandler((request, reply) => this.sendIndex(reply));
return fastify;
}
start(port, logEnabled) {
const fastify = this.build(logEnabled);
fastify.listen({ port: port }, (err, address) => {
if (err) { fastify.log.error(err); process.exit(1) }
});
}
sendIndex(reply) {
const stream = fs.createReadStream(new URL('static/index.html', import.meta.url));
reply.code(200).type('text/html').send(stream);
}
}
Source diff could not be displayed: it is too large. Options to address this: view the blob.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment