Validation

Package, implementation and tests
This commit is contained in:
2024-05-19 22:47:42 +02:00
commit 1179c4b1da
5 changed files with 204 additions and 0 deletions
+6
View File
@@ -0,0 +1,6 @@
.idea/
bun.lockb
package-lock.json
node_modules/
._*
.DS_Store
+83
View File
@@ -0,0 +1,83 @@
import {describe, expect, test} from 'bun:test'
import * as f from '.'
// TODO : Test logging
describe('email', () => {
const emails: [string, boolean][] = [
['pascal@pband.ch', true],
['p@p.ch', true],
[`${'a'.repeat(64)}@${'b'.repeat(60)}.ch`, true],
[`${'a'.repeat(128)}@${'b'.repeat(64)}.ch`, false],
['google.ch@test.com', true],
['pascal+truc@pband.ch', true],
['pascal+.@pband.ch', false],
['pascal+p.@pband.ch', false],
['pascal@pband.', false],
['pascal@pband', false],
['pascal@.ch', false],
['@pband.ch', false],
['prout', false],
['google.ch', false],
]
for (const [email, valid] of emails) {
test(`"${email}" is ${valid ? 'valid' : 'invalid'}`, () => {
const res = expect(f.email_is_valid(email))
if (valid) {
res.toBeTrue()
} else {
res.toBeFalse()
}
})
}
})
describe('Password strength', () => {
// TODO : More passwords lol
const pwds: [string, f.PasswordStrength][] = [
// ['test', f.PasswordStrength.Strong],
['OkPassword', f.PasswordStrength.Easy],
]
for (const pwd of pwds) {
for (const level of [f.PasswordStrength.Weak, f.PasswordStrength.Easy, f.PasswordStrength.Medium, f.PasswordStrength.Strong, f.PasswordStrength.Hard]) {
const should_pass = level <= pwd[1]
test(`"${pwd[0]}" should ${should_pass ? '' : 'not '}pass level ${level}`, () => {
const res = expect(f.password_is_strong(pwd[0], level as f.PasswordStrength))
if (should_pass) {
res.toBeTrue()
} else {
res.toBeFalse()
}
})
}
}
})
describe('Contains HTML', () => {
const texts: [string, boolean][] = [
['Hello, world!', false],
['<html><body><h1>Hello, world!</h1></body></html>', true],
['<html><body><h1>Hello, world!</h1></body></html><script>console.log("Hello, world!")</script>', true],
['Hello <strong>guy !</strong>', true],
['Hello <strong>guy !</strong><script>console.log("Hello, world!")</script>', true],
['Simple balise <br/>', true],
['Simple balise <br />', true],
['Simple balise <br>', true],
['Simple math : 1 < 2', false],
['Simple math : 1 < 2 > 0', false],
]
for (const i in texts) {
const [text, contains] = texts[i]
test(`Text ${i} : should ${contains ? '' : 'not '}contain HTML`, () => {
const res = expect(f.contains_html(text))
if (contains) {
res.toBeTrue()
} else {
res.toBeFalse()
}
})
}
})
+52
View File
@@ -0,0 +1,52 @@
import * as EmailValidator from 'email-validator'
import logger from 'log'
import zxcvbn from 'zxcvbn'
import {parseDocument} from 'htmlparser2'
let _verbose: boolean = false
export function verbose(v: boolean) {
_verbose = v
}
const log = logger('validation')
export enum PasswordStrength {
Weak = 0,
Easy = 1,
Medium = 2,
Strong = 3,
Hard = 4,
}
export function password_is_strong(password: string, strength: PasswordStrength = PasswordStrength.Medium, email?: string) : boolean {
log.trace("Verify password's strength")
if (zxcvbn(password, email !== undefined ? [email] : []).score < strength) {
if (_verbose) log.warn("Password is too weak")
return false
}
return true
}
export function contains_html(text: string) : boolean {
log.trace("Verify if text contains HTML")
if (parseDocument(text).children.some(node => node.nodeType === 1)) {
if (_verbose) log.warn("")
return true
}
return false
}
export function email_is_valid(email: string) : boolean {
log.trace("Verify email's validity")
if (!EmailValidator.validate(email)) {
if (_verbose) log.warn("Invalid email")
return false
}
return true
}
+28
View File
@@ -0,0 +1,28 @@
{
"name": "validation",
"description": "Various validation for web usage",
"version": "1.0.0",
"author": "Pascal Perrenoud <pascal@pband.ch>",
"module": "index.ts",
"type": "module",
"files": ["index.ts"],
"scripts": {
"test": "bun test"
},
"dependencies": {
"email-validator": "^2.0.4",
"htmlparser2": "^9.1.0",
"log": "git+https://git.pband.ch/typescript/log",
"zxcvbn": "^4.4.2"
},
"devDependencies": {
"@types/bun": "^1.1.2",
"@types/zxcvbn": "^4.4.4"
},
"peerDependencies": {
"typescript": "^5.0.0"
}
}
+35
View File
@@ -0,0 +1,35 @@
{
"compilerOptions": {
"baseUrl": "./src",
// Enable latest features
"lib": ["ESNext","dom"],
"target": "ESNext",
"module": "ESNext",
"moduleDetection": "force",
"allowJs": true,
"checkJs": true,
// Bundler mode
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"noEmit": true,
// Best practices
"strict": true,
"skipLibCheck": true,
"noFallthroughCasesInSwitch": true,
// Some stricter flags (disabled by default)
"noUnusedLocals": false,
"noUnusedParameters": false,
"forceConsistentCasingInFileNames": true,
"noPropertyAccessFromIndexSignature": false
},
"include": [
"index.ts",
"src/**/*.ts"
]
}