chore: switch from jest to vitest (#404)
This commit is contained in:
parent
7365322a5f
commit
cc1f64d23e
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
})
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)))
|
||||
})
|
|
@ -1,9 +0,0 @@
|
|||
import { minifyHTMLLiterals } from 'minify-html-literals'
|
||||
|
||||
export default {
|
||||
processAsync (source, fileName) {
|
||||
return minifyHTMLLiterals(source, {
|
||||
fileName
|
||||
})
|
||||
}
|
||||
}
|
|
@ -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
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)))
|
||||
})
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
19
package.json
19
package.json
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}),
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'))
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
})
|
||||
|
|
|
@ -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')
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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'
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
})
|
||||
|
||||
|
|
|
@ -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' })
|
||||
})
|
||||
})
|
||||
|
|
|
@ -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 })
|
||||
|
|
|
@ -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' } }))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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'
|
||||
|
||||
|
|
|
@ -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()
|
||||
))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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('👍')
|
||||
|
||||
|
|
|
@ -54,7 +54,6 @@ export function basicBeforeEach () {
|
|||
}
|
||||
|
||||
export async function basicAfterEach () {
|
||||
fetch.mockClear()
|
||||
fetch.reset()
|
||||
await tick(20)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
Loading…
Reference in New Issue