@yoann-86/react_test_lab 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/actions/install-node/action.yml +25 -0
- package/.github/workflows/build_test_deploy_react.yml +114 -0
- package/README.md +105 -0
- package/TEST_PLAN.md +94 -0
- package/babel.config.js +6 -0
- package/cypress/e2e/app-spec.cy.js +182 -0
- package/cypress/e2e/navigation.cy.js +101 -0
- package/cypress/fixtures/example.json +5 -0
- package/cypress/support/commands.js +25 -0
- package/cypress/support/e2e.js +17 -0
- package/cypress.config.js +12 -0
- package/dist/App.css +108 -0
- package/dist/App.js +59 -0
- package/dist/App.test.js +225 -0
- package/dist/components/UserForm.js +188 -0
- package/dist/components/UserForm.test.js +291 -0
- package/dist/components/UsersList.js +38 -0
- package/dist/components/UsersList.test.js +134 -0
- package/dist/components/atomic/TextInput.js +42 -0
- package/dist/index.css +13 -0
- package/dist/index.js +23 -0
- package/dist/infra/api.js +27 -0
- package/dist/infra/api.test.js +54 -0
- package/dist/logo.svg +1 -0
- package/dist/reportWebVitals.js +26 -0
- package/dist/setupTests.js +3 -0
- package/dist/utils/module.js +25 -0
- package/dist/utils/module.test.js +78 -0
- package/dist/utils/validator.js +116 -0
- package/dist/utils/validator.test.js +324 -0
- package/jsdoc.config.json +10 -0
- package/package.json +62 -0
- package/public/favicon.ico +0 -0
- package/public/index.html +43 -0
- package/public/logo192.png +0 -0
- package/public/logo512.png +0 -0
- package/public/manifest.json +25 -0
- package/public/robots.txt +3 -0
- package/repo.md +13 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.calculateAge = calculateAge;
|
|
7
|
+
/**
|
|
8
|
+
* Calculates the age of a person in years based on their birth date.
|
|
9
|
+
*
|
|
10
|
+
* @param {object} p An object representing a person, implementing a birth date
|
|
11
|
+
* @returns {number} The age of the person in years
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
function calculateAge(p) {
|
|
15
|
+
if (!p || !p.birth || p.birth === null || p.birth === undefined) {
|
|
16
|
+
throw new Error("missing param");
|
|
17
|
+
} else if (!(p.birth instanceof Date) || isNaN(p.birth.getTime())) {
|
|
18
|
+
throw new Error("bad param");
|
|
19
|
+
} else if (p.birth > new Date()) {
|
|
20
|
+
throw new Error("Persons from the future are not allowed to calculate their age");
|
|
21
|
+
}
|
|
22
|
+
let dateDiff = new Date(Date.now() - p.birth.getTime());
|
|
23
|
+
let age = Math.abs(dateDiff.getUTCFullYear() - 1970);
|
|
24
|
+
return age;
|
|
25
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _module = require("./module.js");
|
|
4
|
+
describe("calculateAge", () => {
|
|
5
|
+
let people20yearsOld;
|
|
6
|
+
let veryOldPerson;
|
|
7
|
+
let martyMcFly;
|
|
8
|
+
let people20yearsOldBornOnLeapDay;
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
const today = new Date();
|
|
11
|
+
const year = today.getFullYear();
|
|
12
|
+
people20yearsOld = {
|
|
13
|
+
birth: new Date(year - 20, 0, 1)
|
|
14
|
+
};
|
|
15
|
+
veryOldPerson = {
|
|
16
|
+
birth: new Date(year - 120, 0, 1)
|
|
17
|
+
};
|
|
18
|
+
martyMcFly = {
|
|
19
|
+
birth: new Date(year + 20, 0, 1)
|
|
20
|
+
};
|
|
21
|
+
people20yearsOldBornOnLeapDay = {
|
|
22
|
+
birth: new Date(year - 20, 1, 29)
|
|
23
|
+
};
|
|
24
|
+
});
|
|
25
|
+
it("should return a correct age", () => {
|
|
26
|
+
expect((0, _module.calculateAge)(people20yearsOld)).toBe(20);
|
|
27
|
+
});
|
|
28
|
+
it("should return the correct age if birth date is in the far past (age < 01/01/1970)", () => {
|
|
29
|
+
expect((0, _module.calculateAge)(veryOldPerson)).toBe(120);
|
|
30
|
+
});
|
|
31
|
+
it("should throw a 'missing param' error", () => {
|
|
32
|
+
expect(() => (0, _module.calculateAge)()).toThrow("missing param");
|
|
33
|
+
});
|
|
34
|
+
it("non-object param throws (e.g. number)", () => {
|
|
35
|
+
expect(() => (0, _module.calculateAge)(123)).toThrow("missing param");
|
|
36
|
+
});
|
|
37
|
+
it("should throw a 'missing param' error if p doesn't have a birth property", () => {
|
|
38
|
+
expect(() => (0, _module.calculateAge)({
|
|
39
|
+
name: "John",
|
|
40
|
+
age: 30
|
|
41
|
+
})).toThrow("missing param");
|
|
42
|
+
});
|
|
43
|
+
it("should return 'missing param' error if birth is null", () => {
|
|
44
|
+
expect(() => (0, _module.calculateAge)({
|
|
45
|
+
birth: null
|
|
46
|
+
})).toThrow("missing param");
|
|
47
|
+
});
|
|
48
|
+
it("should return 'missing param' error if birth is undefined", () => {
|
|
49
|
+
expect(() => (0, _module.calculateAge)({
|
|
50
|
+
birth: undefined
|
|
51
|
+
})).toThrow("missing param");
|
|
52
|
+
});
|
|
53
|
+
it("should return 'bad param' error if birth is not a date", () => {
|
|
54
|
+
expect(() => (0, _module.calculateAge)({
|
|
55
|
+
birth: "not a date"
|
|
56
|
+
})).toThrow("bad param");
|
|
57
|
+
});
|
|
58
|
+
it("should return 'bad param' error if birth is not a valid date", () => {
|
|
59
|
+
expect(() => (0, _module.calculateAge)({
|
|
60
|
+
birth: new Date("31/12/1986")
|
|
61
|
+
})).toThrow("bad param");
|
|
62
|
+
});
|
|
63
|
+
it("should return an error if birth date is in the future", () => {
|
|
64
|
+
expect(() => (0, _module.calculateAge)(martyMcFly)).toThrow("Persons from the future are not allowed to calculate their age");
|
|
65
|
+
});
|
|
66
|
+
it("should return 0 if birth date is the current day", () => {
|
|
67
|
+
const today = new Date();
|
|
68
|
+
const personBornToday = {
|
|
69
|
+
birth: new Date(today.getFullYear(), today.getMonth(), today.getDate())
|
|
70
|
+
};
|
|
71
|
+
expect((0, _module.calculateAge)(personBornToday)).toBe(0);
|
|
72
|
+
});
|
|
73
|
+
it("returns correct age for person born on Feb 29 (19 or 20yo)", () => {
|
|
74
|
+
const isTodayBeforeLeapDay = new Date() < new Date(new Date().getFullYear(), 1, 29);
|
|
75
|
+
const expectedAge = isTodayBeforeLeapDay ? 19 : 20;
|
|
76
|
+
expect((0, _module.calculateAge)(people20yearsOldBornOnLeapDay)).toBe(expectedAge);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.validateAge = validateAge;
|
|
7
|
+
exports.validateEmail = validateEmail;
|
|
8
|
+
exports.validateIndentity = validateIndentity;
|
|
9
|
+
exports.validateName = validateName;
|
|
10
|
+
exports.validateZipCode = validateZipCode;
|
|
11
|
+
var _module = require("./module");
|
|
12
|
+
/**
|
|
13
|
+
* Validates if a person is 18 years old or older based on their birth date.
|
|
14
|
+
*
|
|
15
|
+
* @param {object} p An object representing a person, implementing a birth date
|
|
16
|
+
* @param {Date} p.birth The birth date of the person
|
|
17
|
+
* @returns {boolean} true if the age is 18 or more, false otherwise
|
|
18
|
+
* @throws {Error} "missing param"
|
|
19
|
+
* @throws {Error} "bad param"
|
|
20
|
+
* @throws {Error} "not allowed"
|
|
21
|
+
*/
|
|
22
|
+
function validateAge(p) {
|
|
23
|
+
const age = (0, _module.calculateAge)(p);
|
|
24
|
+
const isAdult = Boolean(age >= 18);
|
|
25
|
+
if (!isAdult) throw new Error("Vous devez être majeur pour vous inscrire");
|
|
26
|
+
return isAdult;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Validates if the given French zip code is valid.
|
|
31
|
+
*
|
|
32
|
+
* @param {object} p An object representing a person, implementing a zipCode
|
|
33
|
+
* @param {string} p.zipCode The zip code to validate
|
|
34
|
+
* @return {boolean} true if the zip code is a valid French zip code, false otherwise
|
|
35
|
+
* @throws {Error} "missing param"
|
|
36
|
+
* @throws {Error} "bad param"
|
|
37
|
+
*/
|
|
38
|
+
function validateZipCode(p) {
|
|
39
|
+
if (!p || !(p instanceof Object)) throw new Error("missing param");
|
|
40
|
+
if (!p.zipCode || p.zipCode.length === 0) throw new Error("Le code postal ne peut pas être vide");
|
|
41
|
+
const {
|
|
42
|
+
zipCode
|
|
43
|
+
} = p;
|
|
44
|
+
const basicZipCodeRegexp = /^[0-9]{5}$/;
|
|
45
|
+
if (typeof zipCode !== "string") throw new Error("bad param");
|
|
46
|
+
if (zipCode.length !== 5 || !basicZipCodeRegexp.test(zipCode)) throw new Error("Le code postal doit comporter 5 chiffres");
|
|
47
|
+
const isValidGenericZipCodeRegexp = /^(0[1-9]|[1-6]\d|7[0-4]|7[6-9]|8\d|9[0-5])\d{3}|97[1-4]\d{2}|976\d{2}|7500[1-9]|7501\d|75020$/;
|
|
48
|
+
if (isValidGenericZipCodeRegexp.test(zipCode)) return true;else throw new Error("Le code postal n'est pas valide");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Validates a single name (firstname or lastname) against allowed pattern.
|
|
53
|
+
*
|
|
54
|
+
* @param {string} name The name to validate
|
|
55
|
+
* @returns {boolean} true if the name is valid
|
|
56
|
+
* @throws {Error} with specific message about forbidden content (digits, special characters)
|
|
57
|
+
*/
|
|
58
|
+
function validateName(name) {
|
|
59
|
+
const validNameRegexp = /^[a-zA-ZÀ-ÿ\s'-]+$/;
|
|
60
|
+
if (!name || name.length === 0) {
|
|
61
|
+
throw new Error("Le champ ne peut pas être vide");
|
|
62
|
+
}
|
|
63
|
+
if (validNameRegexp.test(name)) {
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
if (/[0-9]/.test(name)) {
|
|
67
|
+
throw new Error("Les chiffres sont interdits");
|
|
68
|
+
}
|
|
69
|
+
if (/[_/!@#$€§£%+=^¨&*(),.?"`':;{}|<>[\]\\]/.test(name)) {
|
|
70
|
+
throw new Error("Les caractères spéciaux sont interdits");
|
|
71
|
+
}
|
|
72
|
+
throw new Error("Une erreur inconnue est survenue");
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Validates if the given first name and last name are valid.
|
|
77
|
+
*
|
|
78
|
+
* @param {object} p An object representing a person, implementing a firstname and/or a lastname
|
|
79
|
+
* @param {string} p.firstname The first name to validate (optional if lastname provided)
|
|
80
|
+
* @param {string} p.lastname The last name to validate (optional if firstname provided)
|
|
81
|
+
* @return {boolean} true if the provided name(s) are valid, false otherwise
|
|
82
|
+
* @throws {Error} "missing param" - if both firstname and lastname are missing
|
|
83
|
+
* @throws {Error} "bad param" - if firstname or lastname is not a string
|
|
84
|
+
* @throws {Error} with specific message about forbidden content (digits, special characters)
|
|
85
|
+
*/
|
|
86
|
+
function validateIndentity(p) {
|
|
87
|
+
if (!p || !(p instanceof Object) || !p.firstname || !p.lastname) throw new Error("missing param");
|
|
88
|
+
const {
|
|
89
|
+
firstname,
|
|
90
|
+
lastname
|
|
91
|
+
} = p;
|
|
92
|
+
if (typeof firstname !== "string" || typeof lastname !== "string") throw new Error("bad param");
|
|
93
|
+
const isValid = validateName(firstname) && validateName(lastname);
|
|
94
|
+
return isValid;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Validates if the given email is valid.
|
|
99
|
+
*
|
|
100
|
+
* @param {object} p An object representing a person, implementing an email
|
|
101
|
+
* @param {string} p.email The email to validate
|
|
102
|
+
* @return {boolean} true if the email is valid, false otherwise
|
|
103
|
+
* @throws {Error} "missing param"
|
|
104
|
+
* @throws {Error} "bad param"
|
|
105
|
+
*/
|
|
106
|
+
function validateEmail(p) {
|
|
107
|
+
if (!p || !(p instanceof Object)) throw new Error("missing param");
|
|
108
|
+
if (!p.email || p.email.length === 0) throw new Error("L'email ne peut pas être vide");
|
|
109
|
+
const {
|
|
110
|
+
email
|
|
111
|
+
} = p;
|
|
112
|
+
if (typeof email !== "string") throw new Error("bad param");
|
|
113
|
+
const validEmailRegexp = /^[a-zA-Z0-9._+-]+@[a-zA-Z0-9]+([.-][a-zA-Z0-9]+)*\.[a-zA-Z]{2,}$/;
|
|
114
|
+
if (validEmailRegexp.test(email)) return true;
|
|
115
|
+
throw new Error("Le format de l'email est invalide");
|
|
116
|
+
}
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _validator = require("./validator.js");
|
|
4
|
+
describe("validateAge", () => {
|
|
5
|
+
let adultPerson;
|
|
6
|
+
let underagePerson;
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
const today = new Date();
|
|
9
|
+
adultPerson = {
|
|
10
|
+
birth: new Date(today.getFullYear() - 20, 0, 1)
|
|
11
|
+
};
|
|
12
|
+
underagePerson = {
|
|
13
|
+
birth: new Date(today.getFullYear() - 15, 0, 1)
|
|
14
|
+
};
|
|
15
|
+
});
|
|
16
|
+
it("should return true for a valid person", () => {
|
|
17
|
+
expect((0, _validator.validateAge)(adultPerson)).toBe(true);
|
|
18
|
+
});
|
|
19
|
+
it("should throw an error for an underage person", () => {
|
|
20
|
+
expect(() => (0, _validator.validateAge)(underagePerson)).toThrow("Vous devez être majeur pour vous inscrire");
|
|
21
|
+
});
|
|
22
|
+
it("should throw a 'missing param' error", () => {
|
|
23
|
+
expect(() => (0, _validator.validateAge)()).toThrow("missing param");
|
|
24
|
+
});
|
|
25
|
+
it("should throw a 'missing param' error if p doesn't have a birth property", () => {
|
|
26
|
+
const personWithoutBirth = {
|
|
27
|
+
name: "John",
|
|
28
|
+
age: 30
|
|
29
|
+
};
|
|
30
|
+
expect(() => (0, _validator.validateAge)(personWithoutBirth)).toThrow("missing param");
|
|
31
|
+
});
|
|
32
|
+
it("should throw a 'missing param' error if birth is null", () => {
|
|
33
|
+
expect(() => (0, _validator.validateAge)({
|
|
34
|
+
birth: null
|
|
35
|
+
})).toThrow("missing param");
|
|
36
|
+
});
|
|
37
|
+
it("should throw a 'missing param' error if birth is undefined", () => {
|
|
38
|
+
expect(() => (0, _validator.validateAge)({
|
|
39
|
+
birth: undefined
|
|
40
|
+
})).toThrow("missing param");
|
|
41
|
+
});
|
|
42
|
+
it("should throw a 'bad param' error if birth is not a date", () => {
|
|
43
|
+
expect(() => (0, _validator.validateAge)({
|
|
44
|
+
birth: "not a date"
|
|
45
|
+
})).toThrow("bad param");
|
|
46
|
+
});
|
|
47
|
+
it("should throw a 'bad param' error if birth is not a valid date", () => {
|
|
48
|
+
expect(() => (0, _validator.validateAge)({
|
|
49
|
+
birth: new Date("invalid date")
|
|
50
|
+
})).toThrow("bad param");
|
|
51
|
+
});
|
|
52
|
+
it("should throw a 'not allowed' error if birth is in the future", () => {
|
|
53
|
+
const futurePerson = {
|
|
54
|
+
birth: new Date(Date.now() + 100000)
|
|
55
|
+
};
|
|
56
|
+
expect(() => (0, _validator.validateAge)(futurePerson)).toThrow("not allowed");
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
describe("validateZipCode", () => {
|
|
60
|
+
describe.each([{
|
|
61
|
+
zipCode: "75001"
|
|
62
|
+
}, {
|
|
63
|
+
zipCode: "20167"
|
|
64
|
+
}, {
|
|
65
|
+
zipCode: "97139"
|
|
66
|
+
}, {
|
|
67
|
+
zipCode: "97600"
|
|
68
|
+
}, {
|
|
69
|
+
zipCode: "35200"
|
|
70
|
+
}, {
|
|
71
|
+
zipCode: "06000"
|
|
72
|
+
}])("valid zip code", zipObj => {
|
|
73
|
+
it("should return true for ".concat(zipObj.zipCode), () => {
|
|
74
|
+
expect((0, _validator.validateZipCode)(zipObj)).toBe(true);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
describe.each([{
|
|
78
|
+
zipCode: "75000"
|
|
79
|
+
}, {
|
|
80
|
+
zipCode: "97500"
|
|
81
|
+
}, {
|
|
82
|
+
zipCode: "00100"
|
|
83
|
+
}, {
|
|
84
|
+
zipCode: "98000"
|
|
85
|
+
}, {
|
|
86
|
+
zipCode: "99000"
|
|
87
|
+
}, {
|
|
88
|
+
zipCode: "97700"
|
|
89
|
+
}, {
|
|
90
|
+
zipCode: "97800"
|
|
91
|
+
}, {
|
|
92
|
+
zipCode: "97900"
|
|
93
|
+
}])("invalid zip code", zipObj => {
|
|
94
|
+
it("should throw an error for ".concat(zipObj.zipCode), () => {
|
|
95
|
+
expect(() => (0, _validator.validateZipCode)(zipObj)).toThrow("Le code postal n'est pas valide");
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
describe.each([() => (0, _validator.validateZipCode)()])("missing param", testCase => {
|
|
99
|
+
it("should throw a 'missing param' error", () => {
|
|
100
|
+
expect(() => testCase()).toThrow("missing param");
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
describe.each([() => (0, _validator.validateZipCode)({
|
|
104
|
+
zipCode: null
|
|
105
|
+
}), () => (0, _validator.validateZipCode)({
|
|
106
|
+
zipCode: undefined
|
|
107
|
+
}), () => (0, _validator.validateZipCode)({
|
|
108
|
+
name: "John",
|
|
109
|
+
age: 30
|
|
110
|
+
})])("empty zip code", testCase => {
|
|
111
|
+
it("should throw a 'Le code postal ne peut pas \xEAtre vide' error", () => {
|
|
112
|
+
expect(() => testCase()).toThrow("Le code postal ne peut pas être vide");
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
describe.each([{
|
|
116
|
+
zipCode: "9300"
|
|
117
|
+
}, {
|
|
118
|
+
zipCode: "123456"
|
|
119
|
+
}, {
|
|
120
|
+
zipCode: "930000"
|
|
121
|
+
}])("invalid zip code format", zipObj => {
|
|
122
|
+
it("should throw a 'invalid param' error for ".concat(zipObj.zipCode), () => {
|
|
123
|
+
expect(() => (0, _validator.validateZipCode)(zipObj)).toThrow("Le code postal doit comporter 5 chiffres");
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
describe.each([() => (0, _validator.validateZipCode)({
|
|
127
|
+
zipCode: 75001
|
|
128
|
+
})])("bad param", testCase => {
|
|
129
|
+
it("should throw a 'bad param' error", () => {
|
|
130
|
+
expect(() => testCase()).toThrow("bad param");
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
describe("validateIndentity", () => {
|
|
135
|
+
describe.each([{
|
|
136
|
+
firstname: "Pierre",
|
|
137
|
+
lastname: "Dubois"
|
|
138
|
+
}, {
|
|
139
|
+
firstname: "Franck",
|
|
140
|
+
lastname: "Müller"
|
|
141
|
+
}, {
|
|
142
|
+
firstname: "Maria",
|
|
143
|
+
lastname: "García"
|
|
144
|
+
}, {
|
|
145
|
+
firstname: "Noël",
|
|
146
|
+
lastname: "Côté"
|
|
147
|
+
}])("valid identity", identity => {
|
|
148
|
+
it("should return true for ".concat(identity.firstname, " ").concat(identity.lastname), () => {
|
|
149
|
+
expect((0, _validator.validateIndentity)(identity)).toBe(true);
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
describe.each([{
|
|
153
|
+
firstname: "Jhon_random",
|
|
154
|
+
lastname: "Doe"
|
|
155
|
+
}, {
|
|
156
|
+
firstname: "Jean<script>",
|
|
157
|
+
lastname: "Dupont"
|
|
158
|
+
}, {
|
|
159
|
+
firstname: "Marie",
|
|
160
|
+
lastname: "Curie>"
|
|
161
|
+
}, {
|
|
162
|
+
firstname: "Paul:hack",
|
|
163
|
+
lastname: "Martin"
|
|
164
|
+
}, {
|
|
165
|
+
firstname: "Luc/admin",
|
|
166
|
+
lastname: "Lefevre"
|
|
167
|
+
}, {
|
|
168
|
+
firstname: "Anne\\root",
|
|
169
|
+
lastname: "Rousseau"
|
|
170
|
+
}, {
|
|
171
|
+
firstname: "Tom@sql",
|
|
172
|
+
lastname: "Dupuis"
|
|
173
|
+
}, {
|
|
174
|
+
firstname: "Lisa[inject]",
|
|
175
|
+
lastname: "Laurent"
|
|
176
|
+
}, {
|
|
177
|
+
firstname: "Marc{xss}",
|
|
178
|
+
lastname: "Renard"
|
|
179
|
+
}])("invalid carac", identity => {
|
|
180
|
+
it("should throw a invalid carac error for ".concat(identity.firstname, " ").concat(identity.lastname), () => {
|
|
181
|
+
expect(() => (0, _validator.validateIndentity)(identity)).toThrow("Les caractères spéciaux sont interdits");
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
describe.each([() => (0, _validator.validateIndentity)(), () => (0, _validator.validateIndentity)(true), () => (0, _validator.validateIndentity)({
|
|
185
|
+
username: "John",
|
|
186
|
+
age: 30
|
|
187
|
+
}), () => (0, _validator.validateIndentity)({
|
|
188
|
+
lastname: "Doe"
|
|
189
|
+
}), () => (0, _validator.validateIndentity)({
|
|
190
|
+
firstname: null,
|
|
191
|
+
lastname: "Doe"
|
|
192
|
+
}), () => (0, _validator.validateIndentity)({
|
|
193
|
+
firstname: "John",
|
|
194
|
+
lastname: undefined
|
|
195
|
+
}), () => (0, _validator.validateIndentity)({
|
|
196
|
+
firstname: "",
|
|
197
|
+
lastname: "Doe"
|
|
198
|
+
})])("missing param", identity => {
|
|
199
|
+
it("should throw a 'missing param' error for ".concat(JSON.stringify(identity)), () => {
|
|
200
|
+
expect(() => identity()).toThrow("missing param");
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
describe.each([() => (0, _validator.validateIndentity)({
|
|
204
|
+
firstname: 121823,
|
|
205
|
+
lastname: "valid"
|
|
206
|
+
}), () => (0, _validator.validateIndentity)({
|
|
207
|
+
firstname: "John",
|
|
208
|
+
lastname: 456
|
|
209
|
+
})])("bad param", (identity, index) => {
|
|
210
|
+
it("should throw a 'bad param' error for case ".concat(index), () => {
|
|
211
|
+
expect(() => identity()).toThrow("bad param");
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
describe("validateName", () => {
|
|
216
|
+
describe.each(["Pierre", "Franck", "Maria", "Noël", "Jean Dupont", "Anne-Marie", "Élise"])("valid name", name => {
|
|
217
|
+
it("should return true for ".concat(name), () => {
|
|
218
|
+
expect((0, _validator.validateName)(name)).toBe(true);
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
describe.each(["Jhon_random", "Jean<script>", "Paul:hack", "Luc/admin", "Anne\\root", "Tom@sql", "Lisa[inject]", "Marc{xss}"])("invalid name", name => {
|
|
222
|
+
it("should throw an error for ".concat(name), () => {
|
|
223
|
+
expect(() => (0, _validator.validateName)(name)).toThrow("Les caractères spéciaux sont interdits");
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
describe.each(["Rob3rt", "12345"])("name with digits", name => {
|
|
227
|
+
it("should throw an error for ".concat(name), () => {
|
|
228
|
+
expect(() => (0, _validator.validateName)(name)).toThrow("Les chiffres sont interdits");
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
describe.each(["", null, undefined])("empty or null name", name => {
|
|
232
|
+
it("should throw an error for ".concat(name), () => {
|
|
233
|
+
expect(() => (0, _validator.validateName)(name)).toThrow("Le champ ne peut pas être vide");
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
describe.each(["你好", "مرحبا", "Привет", "😀"])("name with non-latin characters", name => {
|
|
237
|
+
it("should throw an error for ".concat(name), () => {
|
|
238
|
+
expect(() => (0, _validator.validateName)(name)).toThrow("Une erreur inconnue est survenue");
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
describe("validateEmail", () => {
|
|
243
|
+
describe.each([{
|
|
244
|
+
email: "user@example.com"
|
|
245
|
+
}, {
|
|
246
|
+
email: "john.doe@company.fr"
|
|
247
|
+
}, {
|
|
248
|
+
email: "marie_claire@domain.co.uk"
|
|
249
|
+
}, {
|
|
250
|
+
email: "contact+newsletter@site.com"
|
|
251
|
+
}, {
|
|
252
|
+
email: "admin123@sub.domain.org"
|
|
253
|
+
}, {
|
|
254
|
+
email: "info@company-name.com"
|
|
255
|
+
}])("valid email", email => {
|
|
256
|
+
it("should return true for ".concat(email), () => {
|
|
257
|
+
expect((0, _validator.validateEmail)(email)).toBe(true);
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
describe.each([{
|
|
261
|
+
email: "invalid.email"
|
|
262
|
+
}, {
|
|
263
|
+
email: "@example.com"
|
|
264
|
+
}, {
|
|
265
|
+
email: "user@"
|
|
266
|
+
}, {
|
|
267
|
+
email: "user @example.com"
|
|
268
|
+
}, {
|
|
269
|
+
email: "user@exam ple.com"
|
|
270
|
+
}, {
|
|
271
|
+
email: "user<script>@example.com"
|
|
272
|
+
}, {
|
|
273
|
+
email: "user@example.com<script>"
|
|
274
|
+
}, {
|
|
275
|
+
email: "user@exam>ple.com"
|
|
276
|
+
}, {
|
|
277
|
+
email: "user:hack@example.com"
|
|
278
|
+
}, {
|
|
279
|
+
email: "user/admin@example.com"
|
|
280
|
+
}, {
|
|
281
|
+
email: "user\\root@example.com"
|
|
282
|
+
}, {
|
|
283
|
+
email: "user[inject]@example.com"
|
|
284
|
+
}, {
|
|
285
|
+
email: "user{xss}@example.com"
|
|
286
|
+
}, {
|
|
287
|
+
email: "user@example..com"
|
|
288
|
+
}, {
|
|
289
|
+
email: "user@@example.com"
|
|
290
|
+
}, {
|
|
291
|
+
email: "user@"
|
|
292
|
+
}, {
|
|
293
|
+
email: "@domain.com"
|
|
294
|
+
}])("invalid email", emailCase => {
|
|
295
|
+
it("should return false for ".concat(emailCase.email), () => {
|
|
296
|
+
expect(() => (0, _validator.validateEmail)(emailCase)).toThrow("Le format de l'email est invalide");
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
describe.each([() => (0, _validator.validateEmail)(), () => (0, _validator.validateEmail)(true)])("missing param", testCase => {
|
|
300
|
+
it("should throw a 'missing param' error", () => {
|
|
301
|
+
expect(() => testCase()).toThrow("missing param");
|
|
302
|
+
});
|
|
303
|
+
});
|
|
304
|
+
describe.each([() => (0, _validator.validateEmail)({}), () => (0, _validator.validateEmail)({
|
|
305
|
+
email: ""
|
|
306
|
+
}), () => (0, _validator.validateEmail)({
|
|
307
|
+
email: null
|
|
308
|
+
}), () => (0, _validator.validateEmail)({
|
|
309
|
+
email: undefined
|
|
310
|
+
})])("empty email", testCase => {
|
|
311
|
+
it("should throw a 'L'email ne peut pas \xEAtre vide' error", () => {
|
|
312
|
+
expect(() => testCase()).toThrow("L'email ne peut pas être vide");
|
|
313
|
+
});
|
|
314
|
+
});
|
|
315
|
+
describe.each([() => (0, _validator.validateEmail)({
|
|
316
|
+
email: 123456
|
|
317
|
+
}), () => (0, _validator.validateEmail)({
|
|
318
|
+
email: true
|
|
319
|
+
})])("bad param", testCase => {
|
|
320
|
+
it("should throw a 'bad param' error for ".concat(testCase), () => {
|
|
321
|
+
expect(() => testCase()).toThrow("bad param");
|
|
322
|
+
});
|
|
323
|
+
});
|
|
324
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@yoann-86/react_test_lab",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"homepage": "https://yoannauroyynov.github.io/react-test-lab/",
|
|
6
|
+
"dependencies": {
|
|
7
|
+
"@testing-library/dom": "^10.4.1",
|
|
8
|
+
"@testing-library/jest-dom": "^6.9.1",
|
|
9
|
+
"@testing-library/react": "^16.3.2",
|
|
10
|
+
"@testing-library/user-event": "^13.5.0",
|
|
11
|
+
"axios": "^1.13.5",
|
|
12
|
+
"react": "^19.2.4",
|
|
13
|
+
"react-dom": "^19.2.4",
|
|
14
|
+
"react-scripts": "5.0.1",
|
|
15
|
+
"web-vitals": "^2.1.4",
|
|
16
|
+
"wouter": "^3.9.0"
|
|
17
|
+
},
|
|
18
|
+
"eslintConfig": {
|
|
19
|
+
"extends": [
|
|
20
|
+
"react-app",
|
|
21
|
+
"react-app/jest"
|
|
22
|
+
]
|
|
23
|
+
},
|
|
24
|
+
"browserslist": {
|
|
25
|
+
"production": [
|
|
26
|
+
">0.2%",
|
|
27
|
+
"not dead",
|
|
28
|
+
"not op_mini all"
|
|
29
|
+
],
|
|
30
|
+
"development": [
|
|
31
|
+
"last 1 chrome version",
|
|
32
|
+
"last 1 firefox version",
|
|
33
|
+
"last 1 safari version"
|
|
34
|
+
]
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@babel/cli": "^7.28.6",
|
|
38
|
+
"@babel/core": "^7.29.0",
|
|
39
|
+
"@babel/plugin-transform-react-jsx": "^7.28.6",
|
|
40
|
+
"cypress": "^15.10.0",
|
|
41
|
+
"jsdoc": "^4.0.5",
|
|
42
|
+
"react-test-renderer": "^19.2.4"
|
|
43
|
+
},
|
|
44
|
+
"jest": {
|
|
45
|
+
"transformIgnorePatterns": [
|
|
46
|
+
"node_modules/(?!wouter)/"
|
|
47
|
+
]
|
|
48
|
+
},
|
|
49
|
+
"publishConfig": {
|
|
50
|
+
"access": "public"
|
|
51
|
+
},
|
|
52
|
+
"scripts": {
|
|
53
|
+
"adduser": "login",
|
|
54
|
+
"cypress": "cypress open",
|
|
55
|
+
"start": "react-scripts start",
|
|
56
|
+
"build": "react-scripts build",
|
|
57
|
+
"build:npm": "NODE_ENV=production && rm -rf dist && mkdir dist && npx babel src --out-dir dist --copy-files",
|
|
58
|
+
"jsdoc": "jsdoc -R ./README.md -c ./jsdoc.config.json -r -d ./public/docs",
|
|
59
|
+
"test": "react-scripts test --coverage --collectCoverageFrom=!src/reportWebVitals.js --collectCoverageFrom=!src/index.js --watchAll=false",
|
|
60
|
+
"eject": "react-scripts eject"
|
|
61
|
+
}
|
|
62
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
7
|
+
<meta name="theme-color" content="#000000" />
|
|
8
|
+
<meta
|
|
9
|
+
name="description"
|
|
10
|
+
content="Web site created using create-react-app"
|
|
11
|
+
/>
|
|
12
|
+
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
|
13
|
+
<!--
|
|
14
|
+
manifest.json provides metadata used when your web app is installed on a
|
|
15
|
+
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
|
16
|
+
-->
|
|
17
|
+
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
|
18
|
+
<!--
|
|
19
|
+
Notice the use of %PUBLIC_URL% in the tags above.
|
|
20
|
+
It will be replaced with the URL of the `public` folder during the build.
|
|
21
|
+
Only files inside the `public` folder can be referenced from the HTML.
|
|
22
|
+
|
|
23
|
+
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
|
24
|
+
work correctly both with client-side routing and a non-root public URL.
|
|
25
|
+
Learn how to configure a non-root public URL by running `npm run build`.
|
|
26
|
+
-->
|
|
27
|
+
<title>React App</title>
|
|
28
|
+
</head>
|
|
29
|
+
<body>
|
|
30
|
+
<noscript>You need to enable JavaScript to run this app.</noscript>
|
|
31
|
+
<div id="root"></div>
|
|
32
|
+
<!--
|
|
33
|
+
This HTML file is a template.
|
|
34
|
+
If you open it directly in the browser, you will see an empty page.
|
|
35
|
+
|
|
36
|
+
You can add webfonts, meta tags, or analytics to this file.
|
|
37
|
+
The build step will place the bundled scripts into the <body> tag.
|
|
38
|
+
|
|
39
|
+
To begin the development, run `npm start` or `yarn start`.
|
|
40
|
+
To create a production bundle, use `npm run build` or `yarn build`.
|
|
41
|
+
-->
|
|
42
|
+
</body>
|
|
43
|
+
</html>
|
|
Binary file
|
|
Binary file
|