chore: switch from jest to vitest (#404)

This commit is contained in:
Nolan Lawson 2024-03-09 13:21:00 -08:00 committed by GitHub
parent 7365322a5f
commit cc1f64d23e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
34 changed files with 1044 additions and 1739 deletions

View File

@ -1,8 +0,0 @@
import * as sass from 'sass'
import { minify } from 'csso'
export function buildStyles () {
const file = './src/picker/styles/picker.scss'
const css = sass.compile(file, { style: 'compressed' }).css
return minify(css).css
}

View File

@ -1,22 +0,0 @@
import { buildStyles } from './buildStyles.js'
import { writeFile, mkdirp } from './fs.js'
import path from 'node:path'
const __dirname = path.dirname(new URL(import.meta.url).pathname)
// Build a file containing the CSS just for Jest, because I can't figure out any better way to do this
async function main () {
const styles = buildStyles()
const targetDir = path.join(__dirname, '../node_modules/.cache/emoji-picker-element')
await mkdirp(targetDir)
await writeFile(
path.join(targetDir, 'styles.js'),
`export default ${JSON.stringify(styles)};`,
'utf8'
)
}
main().catch(err => {
console.error(err)
process.exit(1)
})

View File

@ -0,0 +1,18 @@
import * as sass from 'sass'
import { minify } from 'csso'
export function buildStylesRollupPlugin () {
return {
name: 'build-styles-from-scss',
transform (content, id) {
if (id.includes('picker.scss')) {
const css = sass.compile(id, { style: 'compressed' }).css
const code = `export default ${JSON.stringify(minify(css).css)}`
return {
code,
map: null
}
}
}
}
}

View File

@ -1,52 +0,0 @@
import '@testing-library/jest-dom/jest-globals'
import { jest } from '@jest/globals'
import * as FakeIndexedDB from 'fake-indexeddb'
import { Crypto } from '@peculiar/webcrypto'
import { ResizeObserver } from 'd2l-resize-aware/resize-observer-module.js'
import { deleteDatabase } from '../src/database/databaseLifecycle'
import styles from '../node_modules/.cache/emoji-picker-element/styles.js'
import * as fetchMockJest from 'fetch-mock-jest'
const { IDBFactory, IDBKeyRange } = FakeIndexedDB
// See https://github.com/jsdom/jsdom/issues/3455#issuecomment-1333567714
globalThis.crypto.subtle = new Crypto().subtle
if (!globalThis.performance) {
globalThis.performance = {}
}
if (!globalThis.performance.mark) {
globalThis.performance.mark = () => {}
}
if (!globalThis.performance.measure) {
globalThis.performance.measure = () => {}
}
jest.setTimeout(60000)
globalThis.ResizeObserver = ResizeObserver
process.env.NODE_ENV = 'test'
process.env.STYLES = styles
globalThis.IDBKeyRange = IDBKeyRange
globalThis.indexedDB = new IDBFactory()
// Hack to work around an issue with jest-environment-jsdom https://github.com/jsdom/jsdom/issues/3363
globalThis.structuredClone = globalThis.structuredClone ?? (_ => JSON.parse(JSON.stringify(_)))
beforeAll(() => {
jest.spyOn(globalThis.console, 'log').mockImplementation()
jest.spyOn(globalThis.console, 'warn').mockImplementation()
const fetch = fetchMockJest.default.sandbox()
globalThis.fetch = fetch
globalThis.Response = fetch.Response
})
afterEach(async () => {
// fresh indexedDB for every test
const dbs = await globalThis.indexedDB.databases()
await Promise.all(dbs.map(({ name }) => deleteDatabase(name)))
})

View File

@ -1,9 +0,0 @@
import { minifyHTMLLiterals } from 'minify-html-literals'
export default {
processAsync (source, fileName) {
return minifyHTMLLiterals(source, {
fileName
})
}
}

View File

@ -0,0 +1,14 @@
import { minifyHTMLLiterals } from 'minify-html-literals'
export function minifyHtmlLiteralsRollupPlugin () {
return {
name: 'minify-html-in-tag-template-literals',
transform (content, id) {
if (id.includes('PickerTemplate.js')) {
return minifyHTMLLiterals(content, {
fileName: id
})
}
}
}
}

26
config/vitest.setup.js Normal file
View File

@ -0,0 +1,26 @@
import { vi } from 'vitest'
import '@testing-library/jest-dom/vitest'
import { IDBFactory, IDBKeyRange } from 'fake-indexeddb'
import { ResizeObserver } from 'd2l-resize-aware/resize-observer-module.js'
import { deleteDatabase } from '../src/database/databaseLifecycle'
import fetchMock from 'fetch-mock'
beforeAll(() => {
globalThis.ResizeObserver = ResizeObserver
globalThis.IDBKeyRange = IDBKeyRange
globalThis.indexedDB = new IDBFactory()
vi.spyOn(globalThis.console, 'log').mockImplementation(() => undefined)
vi.spyOn(globalThis.console, 'warn').mockImplementation(() => undefined)
})
beforeEach(() => {
globalThis.fetch = fetchMock.sandbox()
globalThis.Response = fetch.Response
})
afterEach(async () => {
// fresh indexedDB for every test
const dbs = await globalThis.indexedDB.databases()
await Promise.all(dbs.map(({ name }) => deleteDatabase(name)))
})

View File

@ -1,30 +0,0 @@
module.exports = {
testEnvironment: 'jsdom',
testMatch: [
'<rootDir>/test/spec/**/*.{spec,test}.{js,jsx,ts,tsx}'
],
transform: {
'^.*PickerTemplate.js$': './config/minifyHtmlInJest.js'
},
moduleFileExtensions: ['js'],
testPathIgnorePatterns: ['node_modules'],
bail: true,
verbose: true,
silent: false,
setupFilesAfterEnv: [
'<rootDir>/config/jest.setup.js'
],
coverageReporters: ['json', 'lcov', 'text', 'html'],
coveragePathIgnorePatterns: [
'bin/',
'test/'
],
coverageThreshold: {
global: {
statements: 100,
branches: 100,
functions: 100,
lines: 100
}
}
}

View File

@ -44,9 +44,9 @@
"dev:server": "node ./test/adhoc/server.js",
"lint": "standard && stylelint '**/*.scss'",
"lint:fix": "standard --fix && stylelint --fix '**/*.scss'",
"test": "node ./bin/buildStylesForJest.js && NODE_OPTIONS=--experimental-vm-modules jest --runInBand",
"test": "vitest",
"test:adhoc": "node ./test/adhoc/server.js",
"cover": "node ./bin/buildStylesForJest.js && NODE_OPTIONS=--experimental-vm-modules jest --runInBand --coverage",
"cover": "vitest --coverage",
"docs": "node bin/processCustomEmoji.js",
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s",
"version": "run-s changelog docs && git add CHANGELOG.md docs"
@ -71,7 +71,6 @@
},
"homepage": "https://github.com/nolanlawson/emoji-picker-element#readme",
"devDependencies": {
"@peculiar/webcrypto": "^1.4.5",
"@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-inject": "^5.0.5",
"@rollup/plugin-node-resolve": "^15.2.3",
@ -80,6 +79,8 @@
"@testing-library/dom": "^9.3.4",
"@testing-library/jest-dom": "^6.4.2",
"@testing-library/user-event": "^14.5.2",
"@vitest/coverage-istanbul": "^1.3.1",
"@vitest/ui": "^1.3.1",
"blob-util": "^2.0.2",
"compression": "^1.7.4",
"conventional-changelog-cli": "^4.1.0",
@ -91,13 +92,12 @@
"express": "^4.18.3",
"fake-indexeddb": "^5.0.2",
"fast-glob": "^3.3.2",
"fetch-mock-jest": "^1.5.1",
"fetch-mock": "^9.11.0",
"flat-color-icons": "^1.1.0",
"focus-visible": "^5.2.0",
"get-folder-size": "^4.0.0",
"husky": "^9.0.11",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"jsdom": "^24.0.0",
"lint-staged": "^15.2.2",
"lodash-es": "^4.17.15",
"markdown-table": "^3.0.2",
@ -112,15 +112,14 @@
"rollup-plugin-analyzer": "^4.0.0",
"rollup-plugin-terser": "^7.0.2",
"sass": "^1.71.1",
"shx": "^0.3.4",
"standard": "^17.1.0",
"string.prototype.replaceall": "^1.0.9",
"stylelint": "^16.2.1",
"stylelint-config-recommended-scss": "^14.0.0",
"stylelint-scss": "^6.1.0",
"svgo": "^3.2.0",
"tachometer": "^0.7.0",
"terser": "^5.28.1"
"terser": "^5.28.1",
"vitest": "^1.3.1"
},
"//": {
"jsonwebtoken": "comes from tachometer, tachometer is pinned for now due to breaking change, but jsonwebtoken 8 has a vuln"
@ -186,7 +185,7 @@
"*.(css|scss)": "stylelint --fix '**/*.scss'"
},
"volta": {
"node": "20.9.0",
"node": "20.11.1",
"yarn": "1.22.19"
}
}

View File

@ -3,10 +3,10 @@ import resolve from '@rollup/plugin-node-resolve'
import replace from '@rollup/plugin-replace'
import strip from '@rollup/plugin-strip'
import analyze from 'rollup-plugin-analyzer'
import { buildStyles } from './bin/buildStyles.js'
import { minifyHTMLLiterals } from 'minify-html-literals'
import { minifyHtmlLiteralsRollupPlugin } from './config/minifyHtmlLiteralsRollupPlugin.js'
import { buildStylesRollupPlugin } from './config/buildStylesRollupPlugin.js'
const { NODE_ENV, DEBUG } = process.env
const { NODE_ENV, DEBUG, PERF } = process.env
const dev = NODE_ENV !== 'production'
// Build Database.test.js and Picker.js as separate modules at build times so that they are properly tree-shakeable.
@ -16,9 +16,8 @@ const baseConfig = {
resolve(),
cjs(),
replace({
'process.env.NODE_ENV': dev ? '"development"' : '"production"',
'process.env.PERF': !!process.env.PERF,
'process.env.STYLES': JSON.stringify(buildStyles()),
'import.meta.env.MODE': dev ? '"development"' : '"production"',
'import.meta.env.PERF': !!PERF,
preventAssignment: true
}),
replace({
@ -26,20 +25,12 @@ const baseConfig = {
delimiters: ['', ''],
preventAssignment: true
}),
{
name: 'minify-html-in-tag-template-literals',
transform (content, id) {
if (id.includes('PickerTemplate.js')) {
return minifyHTMLLiterals(content, {
fileName: id
})
}
}
},
minifyHtmlLiteralsRollupPlugin(),
buildStylesRollupPlugin(),
strip({
include: ['**/*.js'],
functions: [
(!dev && !process.env.PERF) && 'performance.*',
(!dev && !PERF) && 'performance.*',
!dev && 'console.log'
].filter(Boolean)
}),

View File

@ -4,7 +4,13 @@ import { binaryStringToArrayBuffer, arrayBufferToBinaryString } from 'blob-util'
export async function jsonChecksum (object) {
performance.mark('jsonChecksum')
const inString = JSON.stringify(object)
const inBuffer = binaryStringToArrayBuffer(inString)
let inBuffer = binaryStringToArrayBuffer(inString)
/* istanbul ignore else */
if (import.meta.env.MODE === 'test') {
// Issue with ArrayBuffer in jsdom https://github.com/vitest-dev/vitest/issues/5365
inBuffer = Buffer.from(new Uint8Array(inBuffer))
}
// this does not need to be cryptographically secure, SHA-1 is fine
const outBuffer = await crypto.subtle.digest('SHA-1', inBuffer)
const outBinString = arrayBufferToBinaryString(outBuffer)

View File

@ -4,6 +4,7 @@ import { DEFAULT_CATEGORY_SORTING, DEFAULT_SKIN_TONE_EMOJI, FONT_FAMILY } from '
import enI18n from './i18n/en.js'
import Database from './ImportedDatabase'
import { queueMicrotask } from './utils/queueMicrotask.js'
import baseStyles from './styles/picker.scss'
const PROPS = [
'customEmoji',
@ -25,7 +26,7 @@ export default class PickerElement extends HTMLElement {
super()
this.attachShadow({ mode: 'open' })
const style = document.createElement('style')
style.textContent = process.env.STYLES + EXTRA_STYLES
style.textContent = baseStyles + EXTRA_STYLES
this.shadowRoot.appendChild(style)
this._ctx = {
// Set defaults

View File

@ -197,7 +197,7 @@ export function createRoot (shadowRoot, props) {
// mount logic
if (!state.emojiVersion) {
detectEmojiSupportLevel().then(level => {
// Can't actually test emoji support in Jest/JSDom, emoji never render in color in Cairo
// Can't actually test emoji support in Jest/Vitest/JSDom, emoji never render in color in Cairo
/* istanbul ignore next */
if (!level) {
state.message = state.i18n.emojiUnsupportedMessage
@ -312,7 +312,7 @@ export function createRoot (shadowRoot, props) {
const { database } = state
const favs = (await Promise.all(MOST_COMMONLY_USED_EMOJI.map(unicode => (
database.getEmojiByUnicodeOrName(unicode)
)))).filter(Boolean) // filter because in Jest tests we don't have all the emoji in the DB
)))).filter(Boolean) // filter because in Jest/Vitest tests we don't have all the emoji in the DB
state.defaultFavoriteEmojis = favs
}
@ -362,7 +362,7 @@ export function createRoot (shadowRoot, props) {
function calculateEmojiGridStyle (node) {
calculateWidth(node, abortSignal, width => {
/* istanbul ignore next */
if (process.env.NODE_ENV !== 'test') { // jsdom throws errors for this kind of fancy stuff
if (import.meta.env.MODE !== 'test') { // jsdom throws errors for this kind of fancy stuff
// read all the style/layout calculations we need to make
const style = getComputedStyle(refs.rootElement)
const newNumColumns = parseInt(style.getPropertyValue('--num-columns'), 10)
@ -467,7 +467,7 @@ export function createRoot (shadowRoot, props) {
createEffect(() => {
// consider initialLoad to be complete when the first tabpanel and favorites are rendered
/* istanbul ignore next */
if (process.env.NODE_ENV !== 'production' || process.env.PERF) {
if (import.meta.env.MODE !== 'production' || import.meta.env.PERF) {
if (state.currentEmojis.length && state.currentFavorites.length && state.initialLoad) {
state.initialLoad = false
requestPostAnimationFrame(() => performance.measure('initialLoad', 'initialLoad'))

View File

@ -6,7 +6,7 @@ const unkeyedSymbol = Symbol('un-keyed')
// for debugging
/* istanbul ignore else */
if (process.env.NODE_ENV !== 'production') {
if (import.meta.env.MODE !== 'production') {
window.parseCache = parseCache
window.domInstancesCache = domInstancesCache
}
@ -37,7 +37,7 @@ function doChildrenNeedRerender (parentNode, newChildren) {
oldChildrenCount++
}
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && oldChildrenCount !== parentNode.children.length) {
if (import.meta.env.MODE !== 'production' && oldChildrenCount !== parentNode.children.length) {
throw new Error('parentNode.children.length is different from oldChildrenCount, it should not be')
}
// if new children length is different from old, we must re-render
@ -94,7 +94,7 @@ function patch (expressions, instanceBindings) {
} else if (expression instanceof Element) { // html tag template returning a DOM element
newNode = expression
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && newNode === targetNode) {
if (import.meta.env.MODE !== 'production' && newNode === targetNode) {
// it seems impossible for the framework to get into this state, may as well assert on it
// worst case scenario is we lose focus if we call replaceWith on the same node
throw new Error('the newNode and targetNode are the same, this should never happen')
@ -140,7 +140,7 @@ function parse (tokens) {
case '<': {
const nextChar = token.charAt(j + 1)
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && !/[/a-z]/.test(nextChar)) {
if (import.meta.env.MODE !== 'production' && !/[/a-z]/.test(nextChar)) {
// we don't need to support comments ('<!') because we always use html-minify-literals
// also we don't support '<' inside tags, e.g. '<div> 2 < 3 </div>'
throw new Error('framework currently only supports a < followed by / or a-z')
@ -161,7 +161,7 @@ function parse (tokens) {
}
case '=': {
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && !withinTag) {
if (import.meta.env.MODE !== 'production' && !withinTag) {
// we don't currently support '=' anywhere but inside a tag, e.g.
// we don't support '<div>2 + 2 = 4</div>'
throw new Error('framework currently does not support = anywhere but inside a tag')
@ -194,7 +194,7 @@ function parse (tokens) {
}
/* istanbul ignore else */
if (process.env.NODE_ENV !== 'production') {
if (import.meta.env.MODE !== 'production') {
// remind myself that this object is supposed to be immutable
Object.freeze(binding)
}
@ -246,7 +246,7 @@ function traverseAndSetupBindings (dom, elementsToBindings) {
: findPlaceholderComment(element, i) // not an attribute binding, so has a placeholder comment
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && !targetNode) {
if (import.meta.env.MODE !== 'production' && !targetNode) {
throw new Error('targetNode should not be undefined')
}
@ -258,7 +258,7 @@ function traverseAndSetupBindings (dom, elementsToBindings) {
}
/* istanbul ignore else */
if (process.env.NODE_ENV !== 'production') {
if (import.meta.env.MODE !== 'production') {
// remind myself that this object is supposed to be monomorphic (for better JS engine perf)
Object.seal(instanceBinding)
}

View File

@ -17,7 +17,7 @@ export function createState (abortSignal) {
return
}
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && recursionDepth === MAX_RECURSION_DEPTH) {
if (import.meta.env.MODE !== 'production' && recursionDepth === MAX_RECURSION_DEPTH) {
throw new Error('max recursion depth, you probably didn\'t mean to do this')
}
const observersToRun = [...dirtyObservers]
@ -82,7 +82,7 @@ export function createState (abortSignal) {
// for debugging
/* istanbul ignore else */
if (process.env.NODE_ENV !== 'production') {
if (import.meta.env.MODE !== 'production') {
window.state = state
}
@ -91,7 +91,7 @@ export function createState (abortSignal) {
destroyed = true
/* istanbul ignore else */
if (process.env.NODE_ENV !== 'production') {
if (import.meta.env.MODE !== 'production') {
delete window.state
}
})

View File

@ -16,7 +16,7 @@ export function parseTemplate (htmlString) {
template.innerHTML = htmlString
/* istanbul ignore next */
if (process.env.NODE_ENV !== 'production') {
if (import.meta.env.MODE !== 'production') {
if (template.content.children.length !== 1) {
throw new Error('only 1 child allowed for now')
}

View File

@ -1,7 +1,7 @@
// get the width of the text inside of a DOM node, via https://stackoverflow.com/a/59525891/680742
export function calculateTextWidth (node) {
/* istanbul ignore else */
if (process.env.NODE_ENV === 'test') {
if (import.meta.env.MODE === 'test') {
return 1
} else {
const range = document.createRange()

View File

@ -15,7 +15,7 @@ export const detectEmojiSupportLevel = () => {
))
/* istanbul ignore else */
if (process.env.NODE_ENV !== 'production') {
if (import.meta.env.MODE !== 'production') {
promise.then(emojiSupportLevel => {
console.log('emoji support level', emojiSupportLevel)
})

View File

@ -8,7 +8,7 @@
import { FONT_FAMILY } from '../constants'
import { versionsAndTestEmoji } from '../../../bin/versionsAndTestEmoji'
// only used in jest tests
// only used in jest/vitest tests
let simulateCanvasError = false
export function setSimulateCanvasError (value) {
simulateCanvasError = value
@ -42,7 +42,7 @@ const compareFeatures = (feature1, feature2) => {
}
export function testColorEmojiSupported (text) {
if (process.env.NODE_ENV === 'test') {
if (import.meta.env.MODE === 'test') {
if (simulateCanvasError) {
throw new Error('canvas error')
} else if (simulateOldBrowser) {
@ -51,7 +51,7 @@ export function testColorEmojiSupported (text) {
.map(([emoji]) => emoji)
.includes(text)
}
return true // avoid using canvas in jest
return true // avoid using canvas in jest/vitest
}
// Render white and black and then compare them to each other and ensure they're the same
// color, and neither one is black. This shows that the emoji was rendered in color.

View File

@ -6,7 +6,7 @@ import { requestAnimationFrame } from './requestAnimationFrame'
export let resizeObserverSupported = typeof ResizeObserver === 'function'
// only used in jest tests
// only used in jest/vitest tests
export const resetResizeObserverSupported = () => {
resizeObserverSupported = typeof ResizeObserver === 'function'
}

View File

@ -19,57 +19,67 @@ describe('database tests', () => {
test('calls GET first and HEAD afterwards', async () => {
let db = new Database({ dataSource: ALL_EMOJI })
await db.ready()
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(ALL_EMOJI, undefined)
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(ALL_EMOJI)
expect(fetch.lastOptions()).toBe(undefined)
await db.close()
db = new Database({ dataSource: ALL_EMOJI })
await db.ready()
await tick(5) // the HEAD request is done asynchronously, so wait for it
expect(fetch).toHaveBeenCalledTimes(2)
expect(fetch).toHaveBeenLastCalledWith(ALL_EMOJI, { method: 'HEAD' })
expect(fetch.calls().length).toBe(2)
expect(fetch.lastUrl()).toBe(ALL_EMOJI)
expect(fetch.lastOptions()).toEqual({ method: 'HEAD' })
await db.delete()
})
test('calls GET first and tries HEAD if ETag unavailable', async () => {
let db = new Database({ dataSource: ALL_EMOJI_NO_ETAG })
await db.ready()
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(ALL_EMOJI_NO_ETAG, undefined)
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(ALL_EMOJI_NO_ETAG)
expect(fetch.lastOptions()).toBe(undefined)
await db.close()
db = new Database({ dataSource: ALL_EMOJI_NO_ETAG })
await db.ready()
await tick(5) // the request is done asynchronously, so wait for it
expect(fetch).toHaveBeenCalledTimes(3)
expect(fetch).toHaveBeenNthCalledWith(2, ALL_EMOJI_NO_ETAG, { method: 'HEAD' })
expect(fetch).toHaveBeenLastCalledWith(ALL_EMOJI_NO_ETAG, undefined)
expect(fetch.calls().length).toBe(3)
expect(fetch.calls().at(-2)[0]).toBe(ALL_EMOJI_NO_ETAG)
expect(fetch.calls().at(-2)[1]).toEqual({ method: 'HEAD' })
expect(fetch.lastUrl()).toBe(ALL_EMOJI_NO_ETAG)
expect(fetch.lastOptions()).toBe(undefined)
await db.delete()
})
test('database deletion actually deletes and causes re-fetch', async () => {
let db = new Database({ dataSource: ALL_EMOJI })
await db.ready()
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(ALL_EMOJI, undefined)
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(ALL_EMOJI)
expect(fetch.lastOptions()).toBe(undefined)
await db.delete()
db = new Database({ dataSource: ALL_EMOJI })
await db.ready()
expect(fetch).toHaveBeenCalledTimes(2)
expect(fetch).toHaveBeenLastCalledWith(ALL_EMOJI, undefined)
expect(fetch.calls().length).toBe(2)
expect(fetch.lastUrl()).toBe(ALL_EMOJI)
expect(fetch.lastOptions()).toBe(undefined)
await db.delete()
})
test('misconfigured server where ETag in GET but not HEAD still works', async () => {
let db = new Database({ dataSource: ALL_EMOJI_MISCONFIGURED_ETAG })
await db.ready()
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(ALL_EMOJI_MISCONFIGURED_ETAG, undefined)
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(ALL_EMOJI_MISCONFIGURED_ETAG)
expect(fetch.lastOptions()).toBe(undefined)
await db.close()
db = new Database({ dataSource: ALL_EMOJI_MISCONFIGURED_ETAG })
await db.ready()
await tick(5) // the request is done asynchronously, so wait for it
expect(fetch).toHaveBeenCalledTimes(3)
expect(fetch).toHaveBeenNthCalledWith(2, ALL_EMOJI_MISCONFIGURED_ETAG, { method: 'HEAD' })
expect(fetch).toHaveBeenLastCalledWith(ALL_EMOJI_MISCONFIGURED_ETAG, undefined)
expect(fetch.calls().length).toBe(3)
expect(fetch.calls().at(-2)[0]).toBe(ALL_EMOJI_MISCONFIGURED_ETAG)
expect(fetch.calls().at(-2)[1]).toEqual({ method: 'HEAD' })
expect(fetch.lastUrl()).toBe(ALL_EMOJI_MISCONFIGURED_ETAG)
expect(fetch.lastOptions()).toBe(undefined)
await db.delete()
})

View File

@ -4,29 +4,32 @@ describe('basic fetch tests', () => {
beforeEach(basicBeforeEach)
afterEach(basicAfterEach)
test('make sure fetch-mock-jest is working correctly', async () => {
expect(fetch).toHaveBeenCalledTimes(0)
test('make sure fetch-mock is working correctly', async () => {
expect(fetch.calls().length).toBe(0)
const resp = await fetch(ALL_EMOJI)
expect(resp.headers.get('etag')).toBe('W/xxx')
expect(await (resp).json()).toEqual(truncatedEmoji)
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(ALL_EMOJI, undefined)
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(ALL_EMOJI)
expect(fetch.lastOptions()).toBe(undefined)
})
test('make sure fetch-mock-jest is working correctly 2', async () => {
expect(fetch).toHaveBeenCalledTimes(0)
test('make sure fetch-mock is working correctly 2', async () => {
expect(fetch.calls().length).toBe(0)
const resp = await fetch(ALL_EMOJI_NO_ETAG)
expect(resp.headers.get('etag')).toBeFalsy()
expect(await (resp).json()).toEqual(truncatedEmoji)
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(ALL_EMOJI_NO_ETAG, undefined)
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(ALL_EMOJI_NO_ETAG)
expect(fetch.lastOptions()).toBe(undefined)
})
test('make sure fetch-mock-jest is working correctly 3', async () => {
expect(fetch).toHaveBeenCalledTimes(0)
test('make sure fetch-mock is working correctly 3', async () => {
expect(fetch.calls().length).toBe(0)
const resp = await fetch(ALL_EMOJI, { method: 'HEAD' })
expect(resp.headers.get('etag')).toBe('W/xxx')
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(ALL_EMOJI, { method: 'HEAD' })
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(ALL_EMOJI)
expect(fetch.lastOptions()).toEqual({ method: 'HEAD' })
})
})

View File

@ -1,18 +1,17 @@
import { jest } from '@jest/globals'
import { vi } from 'vitest'
import { ALL_EMOJI, basicAfterEach, basicBeforeEach } from '../shared'
import Database from '../../../src/database/Database'
describe('offline first', () => {
beforeEach(() => {
basicBeforeEach()
jest.spyOn(console, 'warn').mockImplementation(() => {})
vi.spyOn(console, 'warn').mockImplementation(() => undefined)
})
afterEach(basicAfterEach)
test('basic offline first test', async () => {
let db = new Database({ dataSource: ALL_EMOJI })
await db.close()
fetch.mockClear()
fetch.reset()
fetch.get(ALL_EMOJI, { body: null, status: 500 })

View File

@ -3,7 +3,6 @@ import Database from '../../../src/database/Database'
import allEmoji from 'emoji-picker-element-data/en/emojibase/data.json'
function mockEmoji (dataSource, data, etag) {
fetch.mockClear()
fetch.reset()
fetch.get(dataSource, () => new Response(JSON.stringify(data), etag && { headers: { ETag: etag } }))
fetch.head(dataSource, () => new Response(null, etag && { headers: { ETag: etag } }))
@ -51,21 +50,25 @@ describe('database second load and update', () => {
await testDataChange(truncatedEmoji, changedEmoji, async (db, dataSource) => {
// first load
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(dataSource, undefined)
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(dataSource)
expect(fetch.lastOptions()).toBe(undefined)
expect((await db.getEmojiByShortcode('rofl')).annotation).toBe('rolling on the floor laughing')
expect(await db.getEmojiByShortcode('weary_cat')).toBeFalsy()
}, async (db, dataSource) => {
// second load
expect(fetch).toHaveBeenCalledTimes(2)
expect(fetch).toHaveBeenLastCalledWith(dataSource, undefined)
expect(fetch).toHaveBeenNthCalledWith(1, dataSource, { method: 'HEAD' })
expect(fetch.calls().length).toBe(2)
expect(fetch.lastUrl()).toBe(dataSource)
expect(fetch.lastOptions()).toBe(undefined)
expect(fetch.calls().at(-2)[0]).toBe(dataSource)
expect(fetch.calls().at(-2)[1]).toEqual({ method: 'HEAD' })
expect((await db.getEmojiByShortcode('rofl'))).toBeFalsy()
expect((await db.getEmojiByShortcode('pineapple')).annotation).toBe('pineapple')
}, async (db, dataSource) => {
// third load
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(dataSource, { method: 'HEAD' })
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(dataSource)
expect(fetch.lastOptions()).toEqual({ method: 'HEAD' })
expect((await db.getEmojiByShortcode('rofl'))).toBeFalsy()
expect((await db.getEmojiByShortcode('pineapple')).annotation).toBe('pineapple')
})
@ -133,8 +136,9 @@ describe('database second load and update', () => {
let db = new Database({ dataSource })
await db.ready()
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(dataSource, undefined)
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(dataSource)
expect(fetch.lastOptions()).toBe(undefined)
expect((await db.getEmojiByShortcode('rofl')).annotation).toBe('rolling on the floor laughing')
expect(await db.getEmojiByShortcode('weary_cat')).toBeFalsy()
@ -151,9 +155,11 @@ describe('database second load and update', () => {
db = new Database({ dataSource })
await db.ready()
await db._lazyUpdate
expect(fetch).toHaveBeenCalledTimes(2)
expect(fetch).toHaveBeenLastCalledWith(dataSource, undefined)
expect(fetch).toHaveBeenNthCalledWith(1, dataSource, { method: 'HEAD' })
expect(fetch.calls().length).toBe(2)
expect(fetch.lastUrl()).toBe(dataSource)
expect(fetch.lastOptions()).toBe(undefined)
expect(fetch.calls().at(-2)[0]).toBe(dataSource)
expect(fetch.calls().at(-2)[1]).toEqual({ method: 'HEAD' })
expect((await db.getEmojiByShortcode('rofl'))).toBeFalsy()
expect((await db.getEmojiByShortcode('pineapple')).annotation).toBe('pineapple')
await db.close()
@ -164,9 +170,11 @@ describe('database second load and update', () => {
db = new Database({ dataSource })
await db.ready()
await db._lazyUpdate
expect(fetch).toHaveBeenCalledTimes(2)
expect(fetch).toHaveBeenLastCalledWith(dataSource, undefined)
expect(fetch).toHaveBeenNthCalledWith(1, dataSource, { method: 'HEAD' })
expect(fetch.calls().length).toBe(2)
expect(fetch.lastUrl()).toBe(dataSource)
expect(fetch.lastOptions()).toBe(undefined)
expect(fetch.calls().at(-2)[0]).toBe(dataSource)
expect(fetch.calls().at(-2)[1]).toEqual({ method: 'HEAD' })
expect((await db.getEmojiByShortcode('rofl'))).toBeFalsy()
expect((await db.getEmojiByShortcode('pineapple')).annotation).toBe('pineapple')
await db.delete()
@ -182,8 +190,9 @@ describe('database second load and update', () => {
let db = new Database({ dataSource })
await db.ready()
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(dataSource, undefined)
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(dataSource)
expect(fetch.lastOptions()).toBe(undefined)
expect((await db.getEmojiByShortcode('rofl')).annotation).toBe('rolling on the floor laughing')
expect(await db.getEmojiByShortcode('weary_cat')).toBeFalsy()
@ -195,7 +204,6 @@ describe('database second load and update', () => {
changedEmoji[roflIndex] = allEmoji.find(_ => _.annotation === 'pineapple') // replace rofl
// second time - update, data is v2
fetch.mockClear()
fetch.reset()
fetch.get(dataSource2, () => new Response(JSON.stringify(changedEmoji), { headers: { ETag: 'W/yyy' } }))
fetch.head(dataSource2, () => new Response(null, { headers: { ETag: 'W/yyy' } }))
@ -203,14 +211,15 @@ describe('database second load and update', () => {
db = new Database({ dataSource: dataSource2 })
await db.ready()
await db._lazyUpdate
expect(fetch).toHaveBeenCalledTimes(2)
expect(fetch).toHaveBeenLastCalledWith(dataSource2, undefined)
expect(fetch).toHaveBeenNthCalledWith(1, dataSource2, { method: 'HEAD' })
expect(fetch.calls().length).toBe(2)
expect(fetch.lastUrl()).toBe(dataSource2)
expect(fetch.lastOptions()).toBe(undefined)
expect(fetch.calls().at(-2)[0]).toBe(dataSource2)
expect(fetch.calls().at(-2)[1]).toEqual({ method: 'HEAD' })
expect((await db.getEmojiByShortcode('rofl'))).toBeFalsy()
expect((await db.getEmojiByShortcode('pineapple')).annotation).toBe('pineapple')
// third time - no update, data is v2
fetch.mockClear()
fetch.reset()
fetch.get(dataSource2, () => new Response(JSON.stringify(changedEmoji), { headers: { ETag: 'W/yyy' } }))
fetch.head(dataSource2, () => new Response(null, { headers: { ETag: 'W/yyy' } }))
@ -218,8 +227,9 @@ describe('database second load and update', () => {
db = new Database({ dataSource: dataSource2 })
await db.ready()
await db._lazyUpdate
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(dataSource2, { method: 'HEAD' })
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(dataSource2)
expect(fetch.lastOptions()).toEqual({ method: 'HEAD' })
expect((await db.getEmojiByShortcode('rofl'))).toBeFalsy()
expect((await db.getEmojiByShortcode('pineapple')).annotation).toBe('pineapple')
@ -236,9 +246,11 @@ describe('database second load and update', () => {
db = new Database({ dataSource: otherSource })
await db.ready()
await tick(5) // the request is done asynchronously, so wait for it
expect(fetch).toHaveBeenCalledTimes(2)
expect(fetch).toHaveBeenNthCalledWith(1, otherSource, { method: 'HEAD' })
expect(fetch).toHaveBeenLastCalledWith(otherSource, undefined)
expect(fetch.calls().length).toBe(2)
expect(fetch.calls().at(-2)[0]).toBe(otherSource)
expect(fetch.calls().at(-2)[1]).toEqual({ method: 'HEAD' })
expect(fetch.lastUrl()).toBe(otherSource)
expect(fetch.lastOptions()).toBe(undefined)
await db.delete()
})
@ -251,7 +263,6 @@ describe('database second load and update', () => {
changedEmoji[roflIndex] = allEmoji.find(_ => _.annotation === 'pineapple') // replace rofl
// second time - update, data is v2
fetch.mockClear()
fetch.reset()
fetch.get(ALL_EMOJI, () => new Response(JSON.stringify(changedEmoji), { headers: { ETag: 'W/yyy' } }))
fetch.head(ALL_EMOJI, () => new Response(null, { headers: { ETag: 'W/yyy' } }))

View File

@ -31,8 +31,9 @@ describe('attributes tests', () => {
document.body.appendChild(picker)
await tick(20)
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(FR_EMOJI, undefined)
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(FR_EMOJI)
expect(fetch.lastOptions()).toBe(undefined)
expect(picker.locale).toEqual('fr')
expect(picker.dataSource).toEqual(FR_EMOJI)
@ -169,8 +170,9 @@ describe('attributes tests', () => {
expect(getByRole(picker.shadowRoot, 'button', { name: /Choose a skin tone/ }).innerHTML)
.toContain('✌')
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(ALL_EMOJI, undefined)
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(ALL_EMOJI)
expect(fetch.lastOptions()).toBe(undefined)
document.body.removeChild(div)
await tick(20)

View File

@ -16,8 +16,9 @@ describe('constructor', () => {
await waitFor(() => expect(getByRole(container, 'menuitem', { name: /😀/ })).toBeVisible())
expect(getByRole(container, 'menuitem', { name: /😀/ })).toBeVisible()
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(DEFAULT_DATA_SOURCE, undefined)
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(DEFAULT_DATA_SOURCE)
expect(fetch.lastOptions()).toBe(undefined)
document.body.removeChild(picker)
await tick(20)

View File

@ -53,8 +53,9 @@ describe('element tests', () => {
test('changing locale/dataSource prop causes only one network request', async () => {
await tick(120)
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(ALL_EMOJI, undefined)
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(ALL_EMOJI)
expect(fetch.lastOptions()).toBe(undefined)
await type(getByRole(container, 'combobox'), 'monkey face')
await waitFor(() => expect(getAllByRole(container, 'option')).toHaveLength(1), {
timeout: 2000
@ -64,8 +65,9 @@ describe('element tests', () => {
picker.locale = 'fr'
picker.dataSource = FR_EMOJI
await tick(120)
expect(fetch).toHaveBeenCalledTimes(2)
expect(fetch).toHaveBeenLastCalledWith(FR_EMOJI, undefined)
expect(fetch.calls().length).toBe(2)
expect(fetch.lastUrl()).toBe(FR_EMOJI)
expect(fetch.lastOptions()).toBe(undefined)
await clear(getByRole(container, 'combobox'))
await type(getByRole(container, 'combobox'), 'singe tête')
await waitFor(() => expect(getAllByRole(container, 'option')).toHaveLength(1))
@ -74,8 +76,9 @@ describe('element tests', () => {
test('changing locale/dataSource attr causes only one network request', async () => {
await tick(120)
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(ALL_EMOJI, undefined)
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(ALL_EMOJI)
expect(fetch.lastOptions()).toBe(undefined)
await type(getByRole(container, 'combobox'), 'monkey face')
await waitFor(() => expect(getAllByRole(container, 'option')).toHaveLength(1), {
timeout: 2000
@ -85,8 +88,9 @@ describe('element tests', () => {
picker.setAttribute('locale', 'fr')
picker.setAttribute('data-source', FR_EMOJI)
await tick(120)
expect(fetch).toHaveBeenCalledTimes(2)
expect(fetch).toHaveBeenLastCalledWith(FR_EMOJI, undefined)
expect(fetch.calls().length).toBe(2)
expect(fetch.lastUrl()).toBe(FR_EMOJI)
expect(fetch.lastOptions()).toBe(undefined)
await clear(getByRole(container, 'combobox'))
await type(getByRole(container, 'combobox'), 'singe tête')
await waitFor(() => expect(getAllByRole(container, 'option')).toHaveLength(1))

View File

@ -1,4 +1,4 @@
import { jest } from '@jest/globals'
import { vi } from 'vitest'
import Picker from '../../../src/picker/PickerElement'
import { ALL_EMOJI, basicAfterEach, basicBeforeEach, tick, truncatedEmoji } from '../shared'
import Database from '../../../src/database/Database'
@ -9,7 +9,7 @@ describe('errors', () => {
beforeEach(async () => {
await basicBeforeEach()
errorSpy = jest.spyOn(global.console, 'error').mockImplementation()
errorSpy = vi.spyOn(global.console, 'error').mockImplementation(() => undefined)
await tick(40)
})
afterEach(async () => {
@ -19,7 +19,6 @@ describe('errors', () => {
await tick(40)
})
// seems not possible to do
test('throws error when setting the database', async () => {
const picker = new Picker({ dataSource: ALL_EMOJI, locale: 'en' })
document.body.appendChild(picker)
@ -32,7 +31,6 @@ describe('errors', () => {
await tick(20)
})
// can't seem to get jest to ignore these expected errors
test('offline shows an error', async () => {
const dataSource = 'error.json'

View File

@ -1,4 +1,4 @@
import { jest } from '@jest/globals'
import { vi } from 'vitest'
import { basicAfterEach, basicBeforeEach, tick } from '../shared'
import Picker from '../../../src/picker/PickerElement'
import { getByRole, waitFor } from '@testing-library/dom'
@ -20,8 +20,9 @@ describe('lifecycle', () => {
await waitFor(() => expect(getByRole(container, 'menuitem', { name: /😀/ })).toBeVisible())
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(DEFAULT_DATA_SOURCE, undefined)
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(DEFAULT_DATA_SOURCE)
expect(fetch.lastOptions()).toBe(undefined)
document.body.removeChild(picker)
await tick(40)
@ -30,8 +31,9 @@ describe('lifecycle', () => {
await waitFor(() => expect(getByRole(container, 'menuitem', { name: /😀/ })).toBeVisible())
// fetch is called once again after re-insertion
expect(fetch).toHaveBeenCalledTimes(2)
expect(fetch).toHaveBeenLastCalledWith(DEFAULT_DATA_SOURCE, { method: 'HEAD' })
expect(fetch.calls().length).toBe(2)
expect(fetch.lastUrl()).toBe(DEFAULT_DATA_SOURCE)
expect(fetch.lastOptions()).toEqual({ method: 'HEAD' })
document.body.removeChild(picker)
await tick(60)
@ -45,7 +47,7 @@ describe('lifecycle', () => {
await waitFor(() => expect(getByRole(container, 'menuitem', { name: /😀/ })).toBeVisible())
const spy = jest.spyOn(picker.database, 'close')
const spy = vi.spyOn(picker.database, 'close')
document.body.removeChild(picker)
await tick(60)
@ -64,8 +66,9 @@ describe('lifecycle', () => {
await tick(60)
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(DEFAULT_DATA_SOURCE, undefined)
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(DEFAULT_DATA_SOURCE)
expect(fetch.lastOptions()).toBe(undefined)
expect(Object.keys(openIndexedDBRequests).length).toBe(0) // no open IDB connections
})
@ -78,8 +81,9 @@ describe('lifecycle', () => {
await tick(120)
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(DEFAULT_DATA_SOURCE, undefined)
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(DEFAULT_DATA_SOURCE)
expect(fetch.lastOptions()).toBe(undefined)
expect(Object.keys(openIndexedDBRequests).length).toBe(0) // no open IDB connections
})
@ -105,8 +109,9 @@ describe('lifecycle', () => {
document.body.appendChild(picker)
await tick(40)
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(DEFAULT_DATA_SOURCE, undefined)
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(DEFAULT_DATA_SOURCE)
expect(fetch.lastOptions()).toBe(undefined)
await expect(() => (
expect(getByRole(picker.shadowRoot, 'option', { name: /😀/ })).toBeVisible()
))
@ -116,7 +121,7 @@ describe('lifecycle', () => {
await tick(40)
expect(fetch).toHaveBeenCalledTimes(1) // fetch is not called again because no re-render
expect(fetch.calls().length).toBe(1) // fetch is not called again because no re-render
await expect(() => (
expect(getByRole(picker.shadowRoot, 'option', { name: /😀/ })).toBeVisible()
))
@ -132,8 +137,9 @@ describe('lifecycle', () => {
document.body.appendChild(picker)
await tick(40)
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(DEFAULT_DATA_SOURCE, undefined)
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(DEFAULT_DATA_SOURCE)
expect(fetch.lastOptions()).toBe(undefined)
await expect(() => (
expect(getByRole(picker.shadowRoot, 'option', { name: /😀/ })).toBeVisible()
))
@ -144,8 +150,9 @@ describe('lifecycle', () => {
await tick(40)
expect(fetch).toHaveBeenCalledTimes(2) // fetch is called again due to re-render
expect(fetch).toHaveBeenLastCalledWith(DEFAULT_DATA_SOURCE, { method: 'HEAD' }) // cached, so does a HEAD
expect(fetch.calls().length).toBe(2) // fetch is called again due to re-render
expect(fetch.lastUrl()).toBe(DEFAULT_DATA_SOURCE)
expect(fetch.lastOptions()).toEqual({ method: 'HEAD' }) // cached, so does a HEAD
await expect(() => (
expect(getByRole(picker.shadowRoot, 'option', { name: /😀/ })).toBeVisible()
))
@ -166,8 +173,9 @@ describe('lifecycle', () => {
await tick(40)
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(DEFAULT_DATA_SOURCE, undefined)
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(DEFAULT_DATA_SOURCE)
expect(fetch.lastOptions()).toBe(undefined)
await expect(() => (
expect(getByRole(picker.shadowRoot, 'option', { name: /😀/ })).toBeVisible()
))

View File

@ -23,8 +23,9 @@ describe('properties', () => {
await tick(40)
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(FR_EMOJI, undefined)
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(FR_EMOJI)
expect(fetch.lastOptions()).toBe(undefined)
expect(picker.locale).toEqual('fr')
expect(picker.dataSource).toEqual(FR_EMOJI)
@ -43,8 +44,9 @@ describe('properties', () => {
await tick(40)
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(FR_EMOJI, undefined)
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(FR_EMOJI)
expect(fetch.lastOptions()).toBe(undefined)
expect(picker.locale).toEqual('en')
expect(picker.dataSource).toEqual(FR_EMOJI)
@ -63,8 +65,9 @@ describe('properties', () => {
await tick(40)
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(DEFAULT_DATA_SOURCE, undefined)
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(DEFAULT_DATA_SOURCE)
expect(fetch.lastOptions()).toBe(undefined)
expect(picker.locale).toEqual('fr')
expect(picker.dataSource).toEqual(DEFAULT_DATA_SOURCE)

View File

@ -29,7 +29,7 @@ describe('upgrade tests', () => {
await tick(20)
expect(fetch).not.toHaveBeenCalled()
expect(fetch.calls().length).toBe(0)
await import('../../../src/picker/PickerElement')
@ -37,8 +37,9 @@ describe('upgrade tests', () => {
const container = picker.shadowRoot
expect(fetch).toHaveBeenCalledTimes(1)
expect(fetch).toHaveBeenLastCalledWith(FR_EMOJI, undefined)
expect(fetch.calls().length).toBe(1)
expect(fetch.lastUrl()).toBe(FR_EMOJI)
expect(fetch.lastOptions()).toBe(undefined)
expect(getByRole(container, 'button', { name: /Choose a skin tone/ }).innerHTML).toContain('👍')

View File

@ -54,7 +54,6 @@ export function basicBeforeEach () {
}
export async function basicAfterEach () {
fetch.mockClear()
fetch.reset()
await tick(20)
}

30
vitest.config.js Normal file
View File

@ -0,0 +1,30 @@
import { defineConfig } from 'vitest/config'
import { minifyHtmlLiteralsRollupPlugin } from './config/minifyHtmlLiteralsRollupPlugin.js'
import { buildStylesRollupPlugin } from './config/buildStylesRollupPlugin.js'
export default defineConfig({
plugins: [
minifyHtmlLiteralsRollupPlugin(),
buildStylesRollupPlugin()
],
test: {
globals: true,
environment: 'jsdom',
setupFiles: [
'./config/vitest.setup.js'
],
testTimeout: 60000,
coverage: {
provider: 'istanbul',
include: [
'src/'
],
thresholds: {
statements: 100,
branches: 100,
functions: 100,
lines: 100
}
}
}
})

2218
yarn.lock

File diff suppressed because it is too large Load Diff