js: configure js linter (#83187) #393

Open
csechet wants to merge 2 commits from wip/83187-configurer-linter-js into main
6 changed files with 401 additions and 226 deletions

145
.eslintrc.yml Normal file
View File

@ -0,0 +1,145 @@
env:
browser: true
es2017: true
extends: eslint:recommended
parserOptions:
sourceType: module
ecmaVersion: 13
ignorePatterns:
- "passerelle/apps/qrcode/static/qrcode/js/nacl.min.js"
- "passerelle/apps/qrcode/static/qrcode/js/nacl.min.js"
- "passerelle/apps/qrcode/static/qrcode/js/zxing-browser.min.js"
- "passerelle/static/js/passerelle.js"
- "passerelle/static/js/slugify.js"
overrides:
- files: tests/js/**/*.js
env:
node: true
- files: vitest.config.js
env:
node: true
rules:
# Follow Standard JS guidelines : https://standardjs.com/rules.html, except rules
# annotated with a 'custom' comment
# Linting
array-callback-return: error
constructor-super: error
eqeqeq: [error, always, {null: ignore}]
handle-callback-err: error
no-array-constructor: error
no-caller: error
no-class-assign: error
no-cond-assign: error
no-const-assign: error
no-constant-condition: [error, {checkLoops: false}]
no-control-regex: error
no-debugger: error
no-delete-var: error
no-dupe-args: error
no-dupe-class-members: error
no-dupe-keys: error
no-duplicate-case: error
no-duplicate-imports: error
no-empty-character-class: error
no-empty-pattern: error
no-eval: error
no-ex-assign: error
no-extend-native: error
no-extra-boolean-cast: error
no-fallthrough: error
no-func-assign: error
no-global-assign: error
no-implied-eval: error
no-inner-declarations: error
no-invalid-regexp: error
no-iterator: error
no-labels: error
no-new-func: error
no-new-object: error
no-new-require: error
no-new-symbol: error
no-new-wrappers: error
no-new: error
no-obj-calls: error
no-octal-escape: error
no-octal: error
no-proto: error
no-redeclare: error
no-regex-spaces: error
no-return-assign: error
no-self-assign: error
no-self-compare: error
no-sequences: error
no-shadow-restricted-names: error
no-sparse-arrays: error
no-template-curly-in-string: error
no-this-before-super: error
no-throw-literal: error
no-undef: error
no-unexpected-multiline: error
no-unmodified-loop-condition: error
no-unneeded-ternary: error
no-unreachable: error
no-unsafe-finally: error
no-unsafe-negation: error
no-unused-vars: error
no-use-before-define: [error, {functions: false, variables: false, classes: false}]
no-useless-call: error
no-useless-computed-key: error
no-useless-constructor: error
no-useless-escape: error
no-var: error
no-with: error
use-isnan: error
valid-typeof: error
# Style / Formatting
accessor-pairs: error
block-spacing: error
brace-style: [error, 1tbs, {allowSingleLine: true}]
camelcase: error
comma-dangle: [error, always-multiline] # custom : Adding a dangling comma make patches shorter
comma-spacing: error
comma-style: error
curly: [error, multi-line]
dot-location: [error, property]
eol-last: [error, always]
func-call-spacing: error
indent: [error, 2]
key-spacing: error
keyword-spacing: error
max-len: [error, {code: 110}] # custom: configured like this on python projects
new-cap: [error, { newIsCap: true, capIsNew: false}]
new-parens: error
no-extra-parens: [error, functions]
no-floating-decimal: error
no-irregular-whitespace: error
no-lone-blocks: error
no-mixed-spaces-and-tabs: error
no-multi-spaces: error
no-multi-str: error
no-multiple-empty-lines: error
no-tabs: error
no-trailing-spaces: error
no-undef-init: error
no-useless-rename: error
no-whitespace-before-property: error
object-property-newline: [error, { allowMultiplePropertiesPerLine: true }]
one-var: [error, never]
operator-linebreak: [error, before]
padded-blocks: [error, never]
quotes: [error, single]
rest-spread-spacing: error
semi-spacing: error
semi: [error, never]
space-before-function-paren: error
space-in-parens: error
space-infix-ops: error
space-unary-ops: error
spaced-comment: error
template-curly-spacing: error
wrap-iife: [error, any]
yield-star-spacing: [error, {after: true, before: true}]

View File

@ -34,3 +34,13 @@ repos:
rev: v0.3
hooks:
- id: pre-commit-debian
- repo: https://github.com/pre-commit/mirrors-eslint
rev: v8.36.0
hooks:
- id: eslint
files: \.m?js$
types: [file]
args: [--fix]
exclude: |
(?x)^(
)$

View File

@ -4,7 +4,8 @@ import './zxing-browser.min.js'
/* c8 ignore start */
// https://github.com/zxing-js/browser/issues/72
if (window.ZXingBrowser) {
const patchedMediaStreamIsTorchCompatible = window.ZXingBrowser.BrowserCodeReader.mediaStreamIsTorchCompatible
const patchedMediaStreamIsTorchCompatible
= window.ZXingBrowser.BrowserCodeReader.mediaStreamIsTorchCompatible
window.ZXingBrowser.BrowserCodeReader.mediaStreamIsTorchCompatible = (track) => {
return track.getCapabilities && patchedMediaStreamIsTorchCompatible(track)
}
@ -166,7 +167,7 @@ class QRCodeReader extends window.HTMLElement {
return
}
if(this.getAttribute('tally-url') === null) {
if (this.getAttribute('tally-url') === null) {
this.#tally.setAttribute('hidden', true)
}
@ -219,7 +220,7 @@ class QRCodeReader extends window.HTMLElement {
const data = decodeMimeLike(decoded)
const certificateUUID = data.uuid
if(certificateUUID && certificateUUID === this.#currentCertificate) {
if (certificateUUID && certificateUUID === this.#currentCertificate) {
return
}
@ -237,10 +238,10 @@ class QRCodeReader extends window.HTMLElement {
await Promise.all([
this.#data.refresh(data, this.getAttribute('metadata-url'), certificateUUID),
this.#tally.refresh(this.getAttribute('tally-url'), certificateUUID)
this.#tally.refresh(this.getAttribute('tally-url'), certificateUUID),
])
if(this.querySelector('.qrcode-reader-section.error')) {
if (this.querySelector('.qrcode-reader-section.error')) {
this.#popup.classList.add('error')
}
}
@ -270,6 +271,7 @@ class QRCodeReader extends window.HTMLElement {
window.customElements.define('qrcode-reader', QRCodeReader)
/* eslint-disable max-len */
const sectionTemplate = template(`
<div class="qrcode-reader-section">
<div class="qrcode-reader-section--header">
@ -310,6 +312,7 @@ const sectionTemplate = template(`
</div>
</div>
`)
/* eslint-enable max-len */
class QRCodeReaderSection extends HTMLElement {
@ -318,7 +321,7 @@ class QRCodeReaderSection extends HTMLElement {
#errors
#wrapper
connectedCallback() {
connectedCallback () {
this.appendChild(sectionTemplate.content.cloneNode(true))
this.#wrapper = this.querySelector('.qrcode-reader-section')
@ -327,33 +330,32 @@ class QRCodeReaderSection extends HTMLElement {
this.#errors = this.querySelector('.qrcode-reader-section--errors')
}
get contentElement() { return this.#content }
get contentElement () { return this.#content }
setTitle(title) {
setTitle (title) {
this.#title.innerText = title
}
setContent(element) {
setContent (element) {
this.#content.innerHTML = ''
this.#content.appendChild(element)
}
showError(message) {
showError (message) {
this.#wrapper.classList.add('error')
this.#errors.innerHTML = message
}
reset() {
reset () {
this.#wrapper.classList.remove('error')
this.#content.innerHTML = ''
}
async load(callback) {
async load (callback) {
this.#wrapper.classList.add('loading')
try {
await callback()
}
finally {
} finally {
this.#wrapper.classList.remove('loading')
}
}
@ -367,12 +369,12 @@ const validityTemplate = template(`
`)
class QRCodeReaderValidity extends QRCodeReaderSection {
connectedCallback() {
connectedCallback () {
super.connectedCallback()
this.setTitle(translate('validity_section_title'))
}
refresh(validityStart, validityEnd) {
refresh (validityStart, validityEnd) {
this.reset()
validityStart = validityStart && new Date(parseFloat(validityStart) * 1000)
validityEnd = validityEnd && new Date(parseFloat(validityEnd) * 1000)
@ -387,13 +389,18 @@ class QRCodeReaderValidity extends QRCodeReaderSection {
const validityElement = validityTemplate.cloneNode(true)
if (validityStart) {
validityElement.innerHTML = validityElement.innerHTML.replace('{validityStart}', validityStart.toLocaleString())
validityElement.innerHTML = validityElement.innerHTML.replace(
'{validityStart}', validityStart.toLocaleString(),
)
} else {
validityElement.innerHTML = validityElement.innerHTML.replace('{validityStart}', translate('always'))
}
if (validityStart) {
validityElement.innerHTML = validityElement.innerHTML.replace('{validityEnd}', validityEnd.toLocaleString())
validityElement.innerHTML = validityElement.innerHTML.replace(
'{validityEnd}',
validityEnd.toLocaleString(),
)
} else {
validityElement.innerHTML = validityElement.innerHTML.replace('{validityEnd}', translate('never'))
}
@ -416,12 +423,12 @@ const dataItemTemplate = template(`
`)
class QRCodeReaderData extends QRCodeReaderSection {
connectedCallback() {
connectedCallback () {
super.connectedCallback()
this.setTitle(translate('data_section_title'))
}
async refresh(data, metadataUrl, certificateUUID) {
async refresh (data, metadataUrl, certificateUUID) {
this.reset()
const contentElement = dataTemplate.content.cloneNode(true)
const itemsElement = contentElement.querySelector('.qrcode-reader-data--items')
@ -442,19 +449,19 @@ class QRCodeReaderData extends QRCodeReaderSection {
})
}
#addItem(itemsElement, label, value) {
const dataItem = dataItemTemplate.cloneNode(true)
dataItem.innerHTML = dataItem.innerHTML.replace('{label}', label).replace('{value}', value)
itemsElement.append(dataItem.content)
#addItem (itemsElement, label, value) {
const dataItem = dataItemTemplate.cloneNode(true)
dataItem.innerHTML = dataItem.innerHTML.replace('{label}', label).replace('{value}', value)
itemsElement.append(dataItem.content)
}
async #showMetadata(itemsElement, metadataUrl, certificateUUID) {
if(metadataUrl === null) {
async #showMetadata (itemsElement, metadataUrl, certificateUUID) {
if (metadataUrl === null) {
return
}
const metadata = await this.#fetchMetadata(metadataUrl, certificateUUID)
if(!metadata) {
if (!metadata) {
return
}
@ -463,24 +470,22 @@ class QRCodeReaderData extends QRCodeReaderSection {
}
}
async #fetchMetadata(metadataUrl, certificateUUID) {
async #fetchMetadata (metadataUrl, certificateUUID) {
const url = `${metadataUrl}?certificate=${certificateUUID}`
let metadata = undefined
let metadata
try {
const response = await fetch(url)
const content = await response.json()
if(response.ok && content.err === 0) {
if (response.ok && content.err === 0) {
metadata = content.data
}
else {
} else {
this.showError(translate('metadata_api_error'))
}
} catch(err) {
if(err.name === 'NetworkError') {
} catch (err) {
if (err.name === 'NetworkError') {
this.showError(translate('metadata_network_error'))
}
else {
} else {
throw err
}
}
@ -492,13 +497,13 @@ class QRCodeReaderData extends QRCodeReaderSection {
window.customElements.define('qrcode-reader-data', QRCodeReaderData)
class QRCodeReaderTally extends QRCodeReaderSection {
connectedCallback() {
connectedCallback () {
super.connectedCallback()
this.setTitle(translate('tally_section_title'))
}
async refresh(tallyUrl, certificateUUID) {
if(tallyUrl === null) {
async refresh (tallyUrl, certificateUUID) {
if (tallyUrl === null) {
return
}
@ -507,40 +512,37 @@ class QRCodeReaderTally extends QRCodeReaderSection {
await this.load(async () => await this.#showTally(tallyUrl, certificateUUID))
}
async #showTally(tallyUrl, certificateUUID) {
async #showTally (tallyUrl, certificateUUID) {
try {
const response = await fetch(
tallyUrl, {
method: "POST",
method: 'POST',
headers: {
"Content-Type": "application/json",
'Content-Type': 'application/json',
},
body: JSON.stringify({
events: [{
certificate: certificateUUID,
timestamp: Math.floor(Date.now() / 1000)}
]}
timestamp: Math.floor(Date.now() / 1000)},
]},
),
})
})
if(!response.ok) {
if (!response.ok) {
this.showError(translate('tally_api_error'))
}
else {
} else {
const json = await response.json()
if(json.data.stamps[certificateUUID] == 'duplicate') {
if (json.data.stamps[certificateUUID] === 'duplicate') {
this.showError(translate('tally_status_already_seen'))
}
else {
} else {
this.contentElement.innerText = translate('tally_status_ok')
}
}
} catch(err) {
if(err.name == 'NetworkError') {
} catch (err) {
if (err.name === 'NetworkError') {
this.showError(translate('tally_network_error'))
}
else {
} else {
throw err
}
}

View File

@ -1,3 +1,4 @@
/* global clients */
const _EVENTS = 'events'
let db = null
@ -7,7 +8,7 @@ let db = null
let lastUpdateTimestamp = 0
const tallyUrls = new Set()
self.addEventListener('activate', async (event) => {
self.addEventListener('activate', async () => {
try {
console.log('Activating QR code service worker')
db = await openIndexedDb()
@ -85,8 +86,8 @@ async function tally (request) {
err: 0,
data: {
timestamp: lastUpdateTimestamp,
stamps
}
stamps,
},
}))
} catch (error) {
console.log(error)
@ -101,7 +102,7 @@ async function refreshTally (url) {
for (const tallyEvent of await getPendingEvents()) {
tallyEvents.push({
certificate: tallyEvent.certificate,
timestamp: tallyEvent.timestamp
timestamp: tallyEvent.timestamp,
})
}
@ -112,8 +113,8 @@ async function refreshTally (url) {
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
since: lastUpdateTimestamp,
events: tallyEvents
})
events: tallyEvents,
}),
})
if (!response.ok) {

View File

@ -1,47 +1,54 @@
import 'qrcode/qrcode-reader.js'
import { expect, test, vi } from 'vitest'
import ZXingBrowser from 'qrcode/zxing-browser.min.js'
import 'qrcode/zxing-browser.min.js'
import nacl from 'qrcode/nacl.min.js'
// private-key: 98f986e1afe2b546d264e45f00eb3f8d1b331bf3d2fa9e73bea3d8b2c9d90274
//
const okCodeData =
'3%73QJ0UK5G45S4XE0+GEUIKU662$D$K0RTM$4R7L7UX0V19FMFR5A++S6BNFR26 IJ7V15NE1NV2RN+T' +
'VH6%PHBDL*:01/69%EPEDUF7Y47EM6JA7MA79W5DOC$CC4S6G-CBW5C%6$Q6J*6JOCIPC4DC646FM6NE1' +
'AECPED-EDLFF QEGEC9VE634L%6 47%47C%6446D36K/EXVDAVCRWEV2C0/DUF7:967461R67:6OA7Y$5' +
'DE1HEC1WE..DF$DUF7/B8-ED:JCSTDR.C4LE1WE..DF$DUF771904E93DKOEXVDKPCF/DV CP9EIEC*ZC' +
'T34ZA8.Q6$Q6HB8A0'
const okCodeData
= '3%73QJ0UK5G45S4XE0+GEUIKU662$D$K0RTM$4R7L7UX0V19FMFR5A++S6BNFR26 IJ7V15NE1NV2RN+T'
+ 'VH6%PHBDL*:01/69%EPEDUF7Y47EM6JA7MA79W5DOC$CC4S6G-CBW5C%6$Q6J*6JOCIPC4DC646FM6NE1'
+ 'AECPED-EDLFF QEGEC9VE634L%6 47%47C%6446D36K/EXVDAVCRWEV2C0/DUF7:967461R67:6OA7Y$5'
+ 'DE1HEC1WE..DF$DUF7/B8-ED:JCSTDR.C4LE1WE..DF$DUF771904E93DKOEXVDKPCF/DV CP9EIEC*ZC'
+ 'T34ZA8.Q6$Q6HB8A0'
const invalidCodeData = 'https://georges-abitbol.fr'
const invalidSignatureData =
'SVF.WB899RLB0%7NFKM.IWSBLTVZ65OQKD59REJ+ I/IDL960%HL%F 5EVVK397HQGIUK3OAPOP0RF/X' +
'L1FIKJG08J5 LE.G9%EPEDUF7M.C%47SW64DC4W5NF6$CC1S64VCBW53ECQZCGPCSG6C%6YZC:DC-96N' +
'E1AECPED-EDLFF QEGEC9VE634L%6 47%47C%6446D36K/EXVDAVCRWEV2C0/DUF7:967461R67:6OA7' +
'Y$5DE1HEC1WE..DF$DUF7/B8-ED:JCSTDR.C4LE1WE..DF$DUF771904E93DKOEXVDKPCF/DV CP9EIE' +
'C*ZCT34ZA8.Q6$Q6HB8A0'
const invalidSignatureData
= 'SVF.WB899RLB0%7NFKM.IWSBLTVZ65OQKD59REJ+ I/IDL960%HL%F 5EVVK397HQGIUK3OAPOP0RF/X'
+ 'L1FIKJG08J5 LE.G9%EPEDUF7M.C%47SW64DC4W5NF6$CC1S64VCBW53ECQZCGPCSG6C%6YZC:DC-96N'
+ 'E1AECPED-EDLFF QEGEC9VE634L%6 47%47C%6446D36K/EXVDAVCRWEV2C0/DUF7:967461R67:6OA7'
+ 'Y$5DE1HEC1WE..DF$DUF7/B8-ED:JCSTDR.C4LE1WE..DF$DUF771904E93DKOEXVDKPCF/DV CP9EIE'
+ 'C*ZCT34ZA8.Q6$Q6HB8A0'
const certificateWithoutValidity
= ':U8$JSK1IVMJP$E06JCBCVQSIXFZ$HEDJ+S3:%1YN13BHJ$GFGLIN88AL5UNSFG8UG+YUV 2J6701809'
+ 'B3X3PP0TH9CN2R3IYEDB$DKWEOED0%EIEC ED3.DQ34%R83:6NW6XM8ME1.$E8UCE44QIC+96M.C9%6I'
+ 'M6E467W5LA76L6%47G%64W5ZJCYX6J*64EC6:6J$6'
class NetworkError extends Error {
get name () { return 'NetworkError' }
}
const certificateWithoutValidity =
':U8$JSK1IVMJP$E06JCBCVQSIXFZ$HEDJ+S3:%1YN13BHJ$GFGLIN88AL5UNSFG8UG+YUV 2J6701809' +
'B3X3PP0TH9CN2R3IYEDB$DKWEOED0%EIEC ED3.DQ34%R83:6NW6XM8ME1.$E8UCE44QIC+96M.C9%6I' +
'M6E467W5LA76L6%47G%64W5ZJCYX6J*64EC6:6J$6'
const qrcodeReaderTest = test.extend({
mock: async ({ task }, use) => {
let scanCallback = undefined
let scanPromise = new Promise((resolve) => scanCallback = resolve)
// eslint-disable-next-line no-empty-pattern
mock: async ({ }, use) => {
let scanCallback
let scanPromise = new Promise((resolve) => { scanCallback = resolve })
const terminate = Symbol()
class MockBrowserQRCodeReader {
async decodeFromVideoDevice(device, element, callback) {
while(true) {
async decodeFromVideoDevice (device, element, callback) {
while (true) {
const result = await scanPromise
if(result === terminate) {
if (result === terminate) {
return
}
scanPromise = new Promise((resolve) => scanCallback = resolve)
scanPromise = new Promise((resolve) => { scanCallback = resolve })
callback({ text: result })
}
@ -51,7 +58,7 @@ const qrcodeReaderTest = test.extend({
window.nacl = nacl
navigator.mediaDevices = true
const savedZXingBrowser = window.ZXingBrowser
window.ZXingBrowser = { BrowserQRCodeReader : MockBrowserQRCodeReader }
window.ZXingBrowser = { BrowserQRCodeReader: MockBrowserQRCodeReader }
const reader = document.createElement('qrcode-reader')
@ -68,7 +75,7 @@ const qrcodeReaderTest = test.extend({
scan: async (text) => {
scanCallback(text)
await new Promise((resolve) => setTimeout(resolve))
}
},
})
vi.useRealTimers()
@ -81,10 +88,11 @@ const qrcodeReaderTest = test.extend({
window.ZXingBrowser = savedZXingBrowser
navigator.mediaDevices = undefined
window.nacl = undefined
}
},
})
test('qrcode reader shows a warning message if not supported on platform', async ({mock}) => {
// eslint-disable-next-line no-empty-pattern
test('qrcode reader shows a warning message if not supported on platform', async ({}) => {
const reader = document.createElement('qrcode-reader')
reader.setAttribute('verify-key', 'f81af42f9f9422d2393859d40994a42cdb2ef68507f056292ac96d1de1f1af83')
document.append(reader)
@ -228,17 +236,15 @@ qrcodeReaderTest('qrcode reader accepts certificate without validity dates', asy
expect(popup.classList.contains('closed')).toBe(false)
expect(popup.classList.contains('error')).toBe(false)
expect(validity.innerText.trim().split(/\s+/)).toStrictEqual(["from", ":", "always", "to", ":", "never"])
expect(validity.innerText.trim().split(/\s+/)).toStrictEqual(['from', ':', 'always', 'to', ':', 'never'])
})
const qrcodeReaderMetadataTest = qrcodeReaderTest.extend({
loadMetadata: async ({ task, mock }, use) => {
loadMetadata: async ({ mock }, use) => {
const loadMetadata = async (qrCodeData, mockFetch) => {
const { reader, scan } = mock
reader.setAttribute('metadata-url', 'https://orson-welles.io')
let resolveLoadingEnded = undefined
const loadingEnded = new Promise((resolve) => resolveLoadingEnded = resolve)
fetch.mockImplementationOnce(async (url) => {
return mockFetch(url)
})
@ -251,7 +257,7 @@ const qrcodeReaderMetadataTest = qrcodeReaderTest.extend({
}
await use(loadMetadata)
}
},
})
qrcodeReaderMetadataTest('qrcode reader fetches metadata', async ({mock, loadMetadata}) => {
@ -264,12 +270,12 @@ qrcodeReaderMetadataTest('qrcode reader fetches metadata', async ({mock, loadMet
ok: true,
json: () => new Promise((resolve) => {
resolve({'err': 0, 'data': {'Classe': 'Oui', 'Ravioles': 'Non'}})
})
}),
}
})
const section = reader.querySelector('qrcode-reader-data .qrcode-reader-section')
expect(section.classList.contains('loading')).toBe(false)
const section = reader.querySelector('qrcode-reader-data .qrcode-reader-section')
expect(section.classList.contains('loading')).toBe(false)
const labels = reader.querySelectorAll('.qrcode-reader-data--item-label')
@ -285,27 +291,30 @@ qrcodeReaderMetadataTest('qrcode reader fetches metadata', async ({mock, loadMet
})
qrcodeReaderMetadataTest('qrcode reader show feedback on metadata network error', async ({mock, loadMetadata}) => {
const { reader, scan } = mock
await loadMetadata(okCodeData, async (url) => {
throw { name: 'NetworkError' }
qrcodeReaderMetadataTest(
'qrcode reader show feedback on metadata network error',
async ({mock, loadMetadata},
) => {
const { reader } = mock
await loadMetadata(okCodeData, async () => {
throw new NetworkError()
})
const spinner = reader.querySelector('.qrcode-reader--spinner')
expect(spinner).toBe(null)
const section = reader.querySelector('qrcode-reader-data .qrcode-reader-section')
const errors = reader.querySelector('qrcode-reader-data .qrcode-reader-section--errors')
expect(section.classList.contains('error')).toBe(true)
expect(errors.innerText).toBe('metadata_network_error')
})
const spinner = reader.querySelector('.qrcode-reader--spinner')
expect(spinner).toBe(null)
const section = reader.querySelector('qrcode-reader-data .qrcode-reader-section')
const errors = reader.querySelector('qrcode-reader-data .qrcode-reader-section--errors')
expect(section.classList.contains('error')).toBe(true)
expect(errors.innerText).toBe('metadata_network_error')
})
qrcodeReaderMetadataTest('qrcode reader show feedback on http error', async ({mock, loadMetadata}) => {
const { reader, scan } = mock
await loadMetadata(okCodeData, async (url) => {
const { reader } = mock
await loadMetadata(okCodeData, async () => {
return {
ok: false,
json: () => new Promise((resolve) => resolve({}))
json: () => new Promise((resolve) => resolve({})),
}
})
@ -319,14 +328,14 @@ qrcodeReaderMetadataTest('qrcode reader show feedback on http error', async ({mo
})
qrcodeReaderMetadataTest('qrcode reader show feedback on api error', async ({mock, loadMetadata}) => {
const { reader, scan } = mock
const { reader } = mock
await loadMetadata(okCodeData, async (url) => {
await loadMetadata(okCodeData, async () => {
return {
ok: true,
json: () => new Promise((resolve) => {
resolve({'err': 1})
})
}),
}
})
@ -339,58 +348,61 @@ qrcodeReaderMetadataTest('qrcode reader show feedback on api error', async ({moc
expect(errors.innerText.trim()).toBe('metadata_api_error')
})
qrcodeReaderMetadataTest("qrcode reader doesn't refetch metadata for the same certificate if popup is opened", async ({mock, loadMetadata}) => {
const { reader, scan } = mock
qrcodeReaderMetadataTest(
'qrcode reader doesn\'t refetch metadata for the same certificate if popup is opened',
async ({mock, loadMetadata},
) => {
const { reader } = mock
await loadMetadata(okCodeData, async (url) => {
return {
ok: true,
json: async () => ({'err': 0, 'data': {'Classe': 'Oui', 'Ravioles': 'Non'}})
}
await loadMetadata(okCodeData, async () => {
return {
ok: true,
json: async () => ({'err': 0, 'data': {'Classe': 'Oui', 'Ravioles': 'Non'}}),
}
})
expect(reader.querySelectorAll('.qrcode-reader-data--item-value').length).toStrictEqual(5)
// loading the same certificate doesn't refeches metadata
await loadMetadata(okCodeData, async () => {
expect.unreachable()
})
expect(reader.querySelectorAll('.qrcode-reader-data--item-value').length).toStrictEqual(5)
// loading another certificate refetches metadata
await loadMetadata(certificateWithoutValidity, async () => {
return {
ok: true,
json: async () => ({'err': 0, 'data': {'Classe': 'Oui', 'Ravioles': 'Non'}}),
}
})
expect(reader.querySelectorAll('.qrcode-reader-data--item-value').length).toStrictEqual(3)
const closeButton = reader.querySelector('.qrcode-reader--close-popup-button')
closeButton.dispatchEvent(new Event('click'))
// metadata is refetched for the same certificate after closing the popup
await loadMetadata(certificateWithoutValidity, async () => {
return {
ok: true,
json: async () => ({'err': 0, 'data': {'Classe': 'Oui', 'Ravioles': 'Non'}}),
}
})
expect(reader.querySelectorAll('.qrcode-reader-data--item-value').length).toStrictEqual(3)
})
expect(reader.querySelectorAll('.qrcode-reader-data--item-value').length).toStrictEqual(5)
// loading the same certificate doesn't refeches metadata
await loadMetadata(okCodeData, async (url) => {
expect.unreachable()
})
expect(reader.querySelectorAll('.qrcode-reader-data--item-value').length).toStrictEqual(5)
// loading another certificate refetches metadata
await loadMetadata(certificateWithoutValidity, async (url) => {
return {
ok: true,
json: async () => ({'err': 0, 'data': {'Classe': 'Oui', 'Ravioles': 'Non'}})
}
})
expect(reader.querySelectorAll('.qrcode-reader-data--item-value').length).toStrictEqual(3)
const closeButton = reader.querySelector('.qrcode-reader--close-popup-button')
closeButton.dispatchEvent(new Event('click'))
// metadata is refetched for the same certificate after closing the popup
await loadMetadata(certificateWithoutValidity, async (url) => {
return {
ok: true,
json: async () => ({'err': 0, 'data': {'Classe': 'Oui', 'Ravioles': 'Non'}})
}
})
expect(reader.querySelectorAll('.qrcode-reader-data--item-value').length).toStrictEqual(3)
})
qrcodeReaderMetadataTest('retry button refetches metadata', async ({mock, loadMetadata}) => {
const { reader, scan } = mock
const { reader } = mock
await loadMetadata(okCodeData, async (url) => {
await loadMetadata(okCodeData, async () => {
return {
ok: true,
json: () => new Promise((resolve) => {
resolve({'err': 1})
})
}),
}
})
@ -400,10 +412,10 @@ qrcodeReaderMetadataTest('retry button refetches metadata', async ({mock, loadMe
const retryButton = reader.querySelector('.qrcode-reader-data--retry-button')
expect(retryButton).not.toBe(null)
fetch.mockImplementationOnce(async (url) => {
fetch.mockImplementationOnce(async () => {
return {
ok: true,
json: async () => ({'err': 0, 'data': {'Classe': 'Oui', 'Ravioles': 'Non'}})
json: async () => ({'err': 0, 'data': {'Classe': 'Oui', 'Ravioles': 'Non'}}),
}
})
@ -414,12 +426,11 @@ qrcodeReaderMetadataTest('retry button refetches metadata', async ({mock, loadMe
})
const qrcodeReaderTallyTest = qrcodeReaderTest.extend({
loadTally: async ({ task, mock }, use) => {
loadTally: async ({ mock }, use) => {
const loadTally = async (qrCodeData, mockFetch) => {
const { reader, scan } = mock
reader.setAttribute('tally-url', 'https://orson-welles.io')
let resolveLoadingEnded = undefined
fetch.mockImplementationOnce(async (url, {body}) => {
return mockFetch(url, JSON.parse(body))
})
@ -430,42 +441,45 @@ const qrcodeReaderTallyTest = qrcodeReaderTest.extend({
}
await use(loadTally)
}
},
})
qrcodeReaderTallyTest('qrcode reader show tally status for unstamped certificates', async ({mock, loadTally}) => {
const { reader, scan } = mock
qrcodeReaderTallyTest(
'qrcode reader show tally status for unstamped certificates',
async ({mock, loadTally},
) => {
const { reader } = mock
const nowBackup = Date.now
Date.now = () => 25000
await loadTally(okCodeData, async (url, body) => {
expect(body).toStrictEqual({
events: [{
certificate: '853b9497-6c1a-4ff1-8604-6dc7cca9003c',
timestamp: 25
}]
const nowBackup = Date.now
Date.now = () => 25000
await loadTally(okCodeData, async (url, body) => {
expect(body).toStrictEqual({
events: [{
certificate: '853b9497-6c1a-4ff1-8604-6dc7cca9003c',
timestamp: 25,
}],
})
return {
ok: true,
json: async () => ({'err': 0, 'data': {'stamps': {[body.events[0].certificate]: 'ok'}}}),
}
})
return {
ok: true,
json: async () => ({'err': 0, 'data': {'stamps': {[body.events[0].certificate]: 'ok'}}})
}
Date.now = nowBackup
const section = reader.querySelector('qrcode-reader-tally .qrcode-reader-section')
expect(section.classList.contains('error')).toBe(false)
const content = reader.querySelector('qrcode-reader-tally .qrcode-reader-section--content')
expect(content.innerText.trim()).toBe('tally_status_ok')
})
Date.now = nowBackup
const section = reader.querySelector('qrcode-reader-tally .qrcode-reader-section')
expect(section.classList.contains('error')).toBe(false)
const content = reader.querySelector('qrcode-reader-tally .qrcode-reader-section--content')
expect(content.innerText.trim()).toBe('tally_status_ok')
})
qrcodeReaderTallyTest('qrcode reader show error for stamped certificates', async ({mock, loadTally}) => {
const { reader, scan } = mock
const { reader } = mock
await loadTally(okCodeData, async (url, body) => {
return {
ok: true,
json: async () => ({'err': 0, 'data': {'stamps': {[body.events[0].certificate]: 'duplicate'}}})
json: async () => ({'err': 0, 'data': {'stamps': {[body.events[0].certificate]: 'duplicate'}}}),
}
})
@ -477,10 +491,10 @@ qrcodeReaderTallyTest('qrcode reader show error for stamped certificates', async
})
qrcodeReaderTallyTest('qrcode reader show tally when request fails ', async ({mock, loadTally}) => {
const { reader, scan } = mock
const { reader } = mock
await loadTally(okCodeData, async (url, body) => {
throw { name: 'NetworkError' }
await loadTally(okCodeData, async () => {
throw new NetworkError()
})
const section = reader.querySelector('qrcode-reader-tally .qrcode-reader-section')
@ -492,7 +506,7 @@ qrcodeReaderTallyTest('qrcode reader show tally when request fails ', async ({mo
const closeButton = reader.querySelector('.qrcode-reader--close-popup-button')
closeButton.dispatchEvent(new Event('click'))
await loadTally(okCodeData, async (url, body) => {
await loadTally(okCodeData, async () => {
return {
ok: false,
}
@ -502,37 +516,40 @@ qrcodeReaderTallyTest('qrcode reader show tally when request fails ', async ({mo
expect(errors.innerText.trim()).toBe('tally_api_error')
})
qrcodeReaderTallyTest("qrcode reader doesn't refetch tally for the same certificate if popup is opened", async ({mock, loadTally}) => {
const { reader, scan } = mock
qrcodeReaderTallyTest(
'qrcode reader doesn\'t refetch tally for the same certificate if popup is opened',
async ({mock, loadTally},
) => {
const { reader } = mock
await loadTally(okCodeData, async (url, body) => {
return {
ok: true,
json: async () => ({'err': 0, 'data': {'stamps': {[body.events[0].certificate]: 'duplicate'}}})
}
await loadTally(okCodeData, async (url, body) => {
return {
ok: true,
json: async () => ({'err': 0, 'data': {'stamps': {[body.events[0].certificate]: 'duplicate'}}}),
}
})
// loading the same certificate doesn't refeches metadata
await loadTally(okCodeData, async () => {
expect.unreachable()
})
// loading another certificate refetches tally status
await loadTally(certificateWithoutValidity, async (url, body) => {
return {
ok: true,
json: async () => ({'err': 0, 'data': {'stamps': {[body.events[0].certificate]: 'ok'}}}),
}
})
const closeButton = reader.querySelector('.qrcode-reader--close-popup-button')
closeButton.dispatchEvent(new Event('click'))
// tally status is refetched for the same certificate after closing the popup
await loadTally(certificateWithoutValidity, async (url, body) => {
return {
ok: true,
json: async () => ({'err': 0, 'data': {'stamps': {[body.events[0].certificate]: 'ok'}}}),
}
})
})
// loading the same certificate doesn't refeches metadata
await loadTally(okCodeData, async () => {
expect.unreachable()
})
// loading another certificate refetches tally status
await loadTally(certificateWithoutValidity, async (url, body) => {
return {
ok: true,
json: async () => ({'err': 0, 'data': {'stamps': {[body.events[0].certificate]: 'ok'}}})
}
})
const closeButton = reader.querySelector('.qrcode-reader--close-popup-button')
closeButton.dispatchEvent(new Event('click'))
// tally status is refetched for the same certificate after closing the popup
await loadTally(certificateWithoutValidity, async (url, body) => {
return {
ok: true,
json: async () => ({'err': 0, 'data': {'stamps': {[body.events[0].certificate]: 'ok'}}})
}
})
})

View File

@ -7,12 +7,12 @@ export default defineConfig({
watchExclude: ['**'],
alias: {
qrcode: fileURLToPath(new URL('./passerelle/apps/qrcode/static/qrcode/js', import.meta.url)),
vitest: process.env.NODE_PATH + '/vitest'
vitest: process.env.NODE_PATH + '/vitest',
},
environment: 'happy-dom',
chaiConfig: {
truncateThreshold: Infinity,
},
}
},
})