test: centralize fetch mocks in one place
This commit is contained in:
parent
55872ba996
commit
49cc5e529f
|
@ -4,6 +4,7 @@ import {
|
|||
ALL_EMOJI_NO_ETAG, tick, mockFrenchDataSource, FR_EMOJI, truncatedEmoji, NO_SHORTCODES, mockDataSourceWithNoShortcodes
|
||||
} from '../shared'
|
||||
import trimEmojiData from '../../../src/trimEmojiData'
|
||||
import { mockFetch, mockGetAndHead } from '../mockFetch.js'
|
||||
|
||||
describe('database tests', () => {
|
||||
beforeEach(basicBeforeEach)
|
||||
|
@ -89,11 +90,11 @@ describe('database tests', () => {
|
|||
const EMPTY = 'empty.json'
|
||||
const NULL_ARRAY = 'null-array.json'
|
||||
const BAD_OBJECT = 'bad-object.json'
|
||||
fetch.get(NULL, () => new Response('null'))
|
||||
fetch.get(NOT_ARRAY, () => new Response('{}'))
|
||||
fetch.get(EMPTY, () => new Response('[]'))
|
||||
fetch.get(NULL_ARRAY, () => new Response('[null]'))
|
||||
fetch.get(BAD_OBJECT, () => new Response('[{"missing": true}]'))
|
||||
mockFetch('get', NULL, 'null')
|
||||
mockFetch('get', NOT_ARRAY, '{}')
|
||||
mockFetch('get', EMPTY, '[]')
|
||||
mockFetch('get', NULL_ARRAY, '[null]')
|
||||
mockFetch('get', BAD_OBJECT, '[{"missing": true}]')
|
||||
|
||||
const makeDB = async (dataSource) => {
|
||||
const db = new Database({ dataSource })
|
||||
|
@ -152,6 +153,7 @@ describe('database tests', () => {
|
|||
await db1.ready()
|
||||
const db2 = new Database({ dataSource: ALL_EMOJI })
|
||||
await db2.ready()
|
||||
await db2._lazyUpdate // TODO [#407] Skipping this causes an InvalidStateError in IDB
|
||||
await db1.close()
|
||||
expect((await db1.getEmojiByUnicodeOrName('🐵')).annotation).toBe('monkey face')
|
||||
await db2.close()
|
||||
|
@ -195,8 +197,7 @@ describe('database tests', () => {
|
|||
test('basic trimEmojiData test', async () => {
|
||||
const trimmed = trimEmojiData(truncatedEmoji)
|
||||
const dataSource = 'trimmed.js'
|
||||
fetch.get(dataSource, () => new Response(JSON.stringify(trimmed), { headers: { ETag: 'W/trim' } }))
|
||||
fetch.head(dataSource, () => new Response(null, { headers: { ETag: 'W/trim' } }))
|
||||
mockGetAndHead(dataSource, trimmed, { headers: { ETag: 'W/trim' } })
|
||||
|
||||
const db = new Database({ dataSource })
|
||||
const emojis = await db.getEmojiBySearchQuery('face')
|
||||
|
|
|
@ -2,6 +2,7 @@ import allEmoji from 'emoji-picker-element-data/en/emojibase/data.json'
|
|||
import Database from '../../../src/database/Database'
|
||||
import { pick, omit } from 'lodash-es'
|
||||
import { basicAfterEach, basicBeforeEach, ALL_EMOJI, truncatedEmoji } from '../shared'
|
||||
import { mockGetAndHead } from '../mockFetch.js'
|
||||
|
||||
// order can change from version to version
|
||||
const expectToBeSorted = results => {
|
||||
|
@ -128,10 +129,7 @@ describe('getEmojiBySearchQuery', () => {
|
|||
|
||||
const EMOJI_WITH_APOS = 'http://localhost/apos.json'
|
||||
|
||||
fetch.get(EMOJI_WITH_APOS, () => new Response(JSON.stringify(emojiWithTwelveOclock), {
|
||||
headers: { ETag: 'W/apos' }
|
||||
}))
|
||||
fetch.head(EMOJI_WITH_APOS, () => new Response(null, { headers: { ETag: 'W/apos' } }))
|
||||
mockGetAndHead(EMOJI_WITH_APOS, emojiWithTwelveOclock, { headers: { ETag: 'W/apos' } })
|
||||
|
||||
const db = new Database({ dataSource: EMOJI_WITH_APOS })
|
||||
|
||||
|
@ -159,10 +157,7 @@ describe('getEmojiBySearchQuery', () => {
|
|||
|
||||
const EMOJI = 'http://localhost/apos.json'
|
||||
|
||||
fetch.get(EMOJI, () => new Response(JSON.stringify(emoji), {
|
||||
headers: { ETag: 'W/blond' }
|
||||
}))
|
||||
fetch.head(EMOJI, () => new Response(null, { headers: { ETag: 'W/blond' } }))
|
||||
mockGetAndHead(EMOJI, emoji, { headers: { ETag: 'W/blond' } })
|
||||
|
||||
const db = new Database({ dataSource: EMOJI })
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { ALL_EMOJI, basicAfterEach, basicBeforeEach, truncatedEmoji } from '../shared'
|
||||
import Database from '../../../src/database/Database'
|
||||
import { mockGetAndHead } from '../mockFetch.js'
|
||||
|
||||
describe('getEmojiByShortcode', () => {
|
||||
beforeEach(basicBeforeEach)
|
||||
|
@ -63,9 +64,7 @@ describe('getEmojiByShortcode', () => {
|
|||
}
|
||||
}
|
||||
|
||||
fetch
|
||||
.get(dataSource, () => new Response(JSON.stringify(emojis), { headers: { ETag: 'W/optional' } }))
|
||||
.head(dataSource, () => new Response(null, { headers: { ETag: 'W/optional' } }))
|
||||
mockGetAndHead(dataSource, emojis, { headers: { ETag: 'W/optional' } })
|
||||
|
||||
const db = new Database({ dataSource })
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import allEmoji from 'emoji-picker-element-data/en/emojibase/data.json'
|
||||
import { ALL_EMOJI, basicAfterEach, basicBeforeEach, truncatedEmoji } from '../shared'
|
||||
import Database from '../../../src/database/Database'
|
||||
import { mockGetAndHead } from '../mockFetch.js'
|
||||
|
||||
describe('getEmojiByUnicode', () => {
|
||||
beforeEach(basicBeforeEach)
|
||||
|
@ -14,10 +15,7 @@ describe('getEmojiByUnicode', () => {
|
|||
]
|
||||
const EMOJI_WITH_PIRATES = 'http://localhost/pirate.json'
|
||||
|
||||
fetch.get(EMOJI_WITH_PIRATES, () => new Response(JSON.stringify(emojiPlusPirateFlag), {
|
||||
headers: { ETag: 'W/yarrr' }
|
||||
}))
|
||||
fetch.head(EMOJI_WITH_PIRATES, () => new Response(null, { headers: { ETag: 'W/yarrr' } }))
|
||||
mockGetAndHead(EMOJI_WITH_PIRATES, emojiPlusPirateFlag, { headers: { ETag: 'W/yarrr' } })
|
||||
|
||||
const db = new Database({ dataSource: EMOJI_WITH_PIRATES })
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { vi } from 'vitest'
|
||||
import { ALL_EMOJI, basicAfterEach, basicBeforeEach } from '../shared'
|
||||
import Database from '../../../src/database/Database'
|
||||
import { mock500GetAndHead } from '../mockFetch.js'
|
||||
|
||||
describe('offline first', () => {
|
||||
beforeEach(() => {
|
||||
|
@ -14,8 +15,7 @@ describe('offline first', () => {
|
|||
await db.close()
|
||||
fetch.reset()
|
||||
|
||||
fetch.get(ALL_EMOJI, { body: null, status: 500 })
|
||||
fetch.head(ALL_EMOJI, { body: null, status: 500 })
|
||||
mock500GetAndHead(ALL_EMOJI)
|
||||
|
||||
db = new Database({ dataSource: ALL_EMOJI })
|
||||
await db.ready()
|
||||
|
@ -27,8 +27,7 @@ describe('offline first', () => {
|
|||
|
||||
test('basic error test', async () => {
|
||||
const ERROR = 'error.json'
|
||||
fetch.get(ERROR, { body: null, status: 500 })
|
||||
fetch.head(ERROR, { body: null, status: 500 })
|
||||
mock500GetAndHead(ERROR)
|
||||
|
||||
const db = new Database({ dataSource: ERROR })
|
||||
await (expect(() => db.ready())).rejects.toThrow()
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import { ALL_EMOJI, ALL_EMOJI_NO_ETAG, basicAfterEach, basicBeforeEach, tick, truncatedEmoji } from '../shared'
|
||||
import Database from '../../../src/database/Database'
|
||||
import allEmoji from 'emoji-picker-element-data/en/emojibase/data.json'
|
||||
import { mockGetAndHead } from '../mockFetch.js'
|
||||
|
||||
function mockEmoji (dataSource, data, etag) {
|
||||
fetch.reset()
|
||||
fetch.get(dataSource, () => new Response(JSON.stringify(data), etag && { headers: { ETag: etag } }))
|
||||
fetch.head(dataSource, () => new Response(null, etag && { headers: { ETag: etag } }))
|
||||
mockGetAndHead(dataSource, data, etag && { headers: { ETag: etag } })
|
||||
}
|
||||
|
||||
async function testDataChange (firstData, secondData, firstCallback, secondCallback, thirdCallback) {
|
||||
|
@ -185,8 +185,7 @@ describe('database second load and update', () => {
|
|||
const dataSource2 = 'http://localhost/will-change2.json'
|
||||
|
||||
// first time - data is v1
|
||||
fetch.get(dataSource, () => new Response(JSON.stringify(truncatedEmoji), { headers: { ETag: 'W/xxx' } }))
|
||||
fetch.head(dataSource, () => new Response(null, { headers: { ETag: 'W/xxx' } }))
|
||||
mockGetAndHead(dataSource, truncatedEmoji, { headers: { ETag: 'W/xxx' } })
|
||||
|
||||
let db = new Database({ dataSource })
|
||||
await db.ready()
|
||||
|
@ -205,8 +204,7 @@ describe('database second load and update', () => {
|
|||
|
||||
// second time - update, data is v2
|
||||
fetch.reset()
|
||||
fetch.get(dataSource2, () => new Response(JSON.stringify(changedEmoji), { headers: { ETag: 'W/yyy' } }))
|
||||
fetch.head(dataSource2, () => new Response(null, { headers: { ETag: 'W/yyy' } }))
|
||||
mockGetAndHead(dataSource2, changedEmoji, { headers: { ETag: 'W/yyy' } })
|
||||
|
||||
db = new Database({ dataSource: dataSource2 })
|
||||
await db.ready()
|
||||
|
@ -221,8 +219,7 @@ describe('database second load and update', () => {
|
|||
|
||||
// third time - no update, data is v2
|
||||
fetch.reset()
|
||||
fetch.get(dataSource2, () => new Response(JSON.stringify(changedEmoji), { headers: { ETag: 'W/yyy' } }))
|
||||
fetch.head(dataSource2, () => new Response(null, { headers: { ETag: 'W/yyy' } }))
|
||||
mockGetAndHead(dataSource2, changedEmoji, { headers: { ETag: 'W/yyy' } })
|
||||
|
||||
db = new Database({ dataSource: dataSource2 })
|
||||
await db.ready()
|
||||
|
@ -264,8 +261,7 @@ describe('database second load and update', () => {
|
|||
|
||||
// second time - update, data is v2
|
||||
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' } }))
|
||||
mockGetAndHead(ALL_EMOJI, changedEmoji, { headers: { ETag: 'W/yyy' } })
|
||||
|
||||
// open two at once
|
||||
const dbs = [
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
// centralize all our fetch mocks in one place so we can have
|
||||
// consistent timeouts, and smooth over some of the boilerplate
|
||||
|
||||
export function mockFetch (method, url, response, { headers, delay } = {}) {
|
||||
let responseToUse
|
||||
if (!response) {
|
||||
responseToUse = null
|
||||
} else if (typeof response === 'string') {
|
||||
responseToUse = response
|
||||
} else {
|
||||
responseToUse = JSON.stringify(response)
|
||||
}
|
||||
|
||||
fetch[method](
|
||||
url,
|
||||
() => new Response(responseToUse, { headers }),
|
||||
// use a delay of 1 because it's more realistic than a fetch() that resolves in a microtask
|
||||
{ delay: typeof delay === 'number' ? delay : 1 }
|
||||
)
|
||||
}
|
||||
|
||||
// convenience util for mocking a typical get and a head
|
||||
export function mockGetAndHead (url, response, options = {}) {
|
||||
mockFetch('get', url, response, options)
|
||||
mockFetch('head', url, null, options)
|
||||
}
|
||||
|
||||
export function mock500GetAndHead (url) {
|
||||
fetch.get(url, { body: null, status: 500 })
|
||||
fetch.head(url, { body: null, status: 500 })
|
||||
}
|
|
@ -13,6 +13,7 @@ import enI18n from '../../../src/picker/i18n/en'
|
|||
import Database from '../../../src/database/Database'
|
||||
import { DEFAULT_SKIN_TONE_EMOJI } from '../../../src/picker/constants'
|
||||
import { DEFAULT_DATA_SOURCE } from '../../../src/database/constants'
|
||||
import { mockGetAndHead } from '../mockFetch.js'
|
||||
const { type } = userEvent
|
||||
|
||||
// Workaround for clear() not working in shadow roots: https://github.com/testing-library/user-event/issues/1143
|
||||
|
@ -123,8 +124,7 @@ describe('element tests', () => {
|
|||
|
||||
describe('defaults test', () => {
|
||||
beforeEach(() => {
|
||||
fetch.get(DEFAULT_DATA_SOURCE, () => new Response(JSON.stringify(truncatedEmoji), { headers: { ETag: 'W/aaa' } }))
|
||||
fetch.head(DEFAULT_DATA_SOURCE, () => new Response(null, { headers: { ETag: 'W/aaa' } }))
|
||||
mockGetAndHead(DEFAULT_DATA_SOURCE, truncatedEmoji, { headers: { ETag: 'W/aaa' } })
|
||||
})
|
||||
|
||||
afterEach(basicAfterEach)
|
||||
|
|
|
@ -3,6 +3,7 @@ import Picker from '../../../src/picker/PickerElement'
|
|||
import { ALL_EMOJI, basicAfterEach, basicBeforeEach, tick, truncatedEmoji } from '../shared'
|
||||
import Database from '../../../src/database/Database'
|
||||
import { getByRole, waitFor } from '@testing-library/dom'
|
||||
import { mock500GetAndHead, mockGetAndHead } from '../mockFetch.js'
|
||||
|
||||
describe('errors', () => {
|
||||
let errorSpy
|
||||
|
@ -34,8 +35,7 @@ describe('errors', () => {
|
|||
test('offline shows an error', async () => {
|
||||
const dataSource = 'error.json'
|
||||
|
||||
fetch.get(dataSource, { body: null, status: 500 })
|
||||
fetch.head(dataSource, { body: null, status: 500 })
|
||||
mock500GetAndHead(dataSource)
|
||||
|
||||
const picker = new Picker({ dataSource })
|
||||
const container = picker.shadowRoot
|
||||
|
@ -55,10 +55,7 @@ describe('errors', () => {
|
|||
test('slow networks show "Loading"', async () => {
|
||||
const dataSource = 'slow.json'
|
||||
|
||||
fetch.get(dataSource, () => new Response(JSON.stringify(truncatedEmoji), { headers: { ETag: 'W/slow' } }),
|
||||
{ delay: 1500 })
|
||||
fetch.head(dataSource, () => new Response(null, { headers: { ETag: 'W/slow' } }),
|
||||
{ delay: 1500 })
|
||||
mockGetAndHead(dataSource, truncatedEmoji, { headers: { ETag: 'W/slow' }, delay: 1500 })
|
||||
|
||||
const picker = new Picker({ dataSource })
|
||||
const container = picker.shadowRoot
|
||||
|
|
|
@ -8,6 +8,7 @@ import allData from 'emoji-picker-element-data/en/emojibase/data.json'
|
|||
import { MOST_COMMONLY_USED_EMOJI } from '../../../src/picker/constants'
|
||||
import { uniqBy } from '../../../src/shared/uniqBy'
|
||||
import { groups } from '../../../src/picker/groups'
|
||||
import { mockGetAndHead } from '../mockFetch.js'
|
||||
|
||||
const dataSource = 'with-favs.json'
|
||||
|
||||
|
@ -23,8 +24,7 @@ describe('Favorites UI', () => {
|
|||
...allData.filter(_ => MOST_COMMONLY_USED_EMOJI.includes(_.emoji))
|
||||
], _ => _.emoji)
|
||||
|
||||
fetch.get(dataSource, () => new Response(JSON.stringify(dataWithFavorites), { headers: { ETag: 'W/favs' } }))
|
||||
fetch.head(dataSource, () => new Response(null, { headers: { ETag: 'W/favs' } }))
|
||||
mockGetAndHead(dataSource, dataWithFavorites, { headers: { ETag: 'W/favs' } })
|
||||
|
||||
picker = new Picker({ dataSource, locale: 'en' })
|
||||
document.body.appendChild(picker)
|
||||
|
|
|
@ -2,6 +2,7 @@ import allEmoji from 'emoji-picker-element-data/en/emojibase/data.json'
|
|||
import frEmoji from 'emoji-picker-element-data/fr/cldr/data.json'
|
||||
import allEmojibaseV5Emoji from 'emojibase-data/en/data.json'
|
||||
import { DEFAULT_DATA_SOURCE } from '../../src/database/constants'
|
||||
import { mockFetch, mockGetAndHead } from './mockFetch.js'
|
||||
|
||||
export function truncateEmoji (allEmoji) {
|
||||
// just take the first few emoji from each category, or else it takes forever to insert
|
||||
|
@ -36,21 +37,11 @@ export const EMOJIBASE_V5 = 'http://localhost/emojibase'
|
|||
export const WITH_ARRAY_SKIN_TONES = 'http://localhost/with-array-skin-tones'
|
||||
|
||||
export function basicBeforeEach () {
|
||||
fetch
|
||||
.get(ALL_EMOJI, () => new Response(JSON.stringify(truncatedEmoji), {
|
||||
headers: { ETag: 'W/xxx' }
|
||||
}))
|
||||
.head(ALL_EMOJI, () => new Response(null, {
|
||||
headers: { ETag: 'W/xxx' }
|
||||
}))
|
||||
.get(ALL_EMOJI_NO_ETAG, truncatedEmoji)
|
||||
.head(ALL_EMOJI_NO_ETAG, () => new Response(null))
|
||||
.get(ALL_EMOJI_MISCONFIGURED_ETAG, () => new Response(JSON.stringify(truncatedEmoji), {
|
||||
headers: { ETag: 'W/xxx' }
|
||||
}))
|
||||
.head(ALL_EMOJI_MISCONFIGURED_ETAG, () => new Response(null))
|
||||
.get(DEFAULT_DATA_SOURCE, () => new Response(JSON.stringify(truncatedEmoji), { headers: { ETag: 'W/def' } }))
|
||||
.head(DEFAULT_DATA_SOURCE, () => new Response(null, { headers: { ETag: 'W/def' } }))
|
||||
mockGetAndHead(ALL_EMOJI, truncatedEmoji, { headers: { ETag: 'W/xxx' } })
|
||||
mockGetAndHead(ALL_EMOJI_NO_ETAG, truncatedEmoji)
|
||||
mockGetAndHead(DEFAULT_DATA_SOURCE, truncatedEmoji, { headers: { ETag: 'W/def' } })
|
||||
mockFetch('get', ALL_EMOJI_MISCONFIGURED_ETAG, truncatedEmoji, { headers: { ETag: 'W/xxx' } })
|
||||
mockFetch('head', ALL_EMOJI_MISCONFIGURED_ETAG, null)
|
||||
}
|
||||
|
||||
export async function basicAfterEach () {
|
||||
|
@ -65,8 +56,7 @@ export async function tick (times = 1) {
|
|||
}
|
||||
|
||||
export function mockFrenchDataSource () {
|
||||
fetch.get(FR_EMOJI, () => new Response(JSON.stringify(truncatedFrEmoji), { headers: { ETag: 'W/zzz' } }))
|
||||
fetch.head(FR_EMOJI, () => new Response(null, { headers: { ETag: 'W/zzz' } }))
|
||||
mockGetAndHead(FR_EMOJI, truncatedFrEmoji, { headers: { ETag: 'W/zzz' } })
|
||||
}
|
||||
|
||||
export function mockDataSourceWithNoShortcodes () {
|
||||
|
@ -75,22 +65,16 @@ export function mockDataSourceWithNoShortcodes () {
|
|||
delete res.shortcodes
|
||||
return res
|
||||
})
|
||||
fetch.get(NO_SHORTCODES, () => new Response(JSON.stringify(noShortcodeEmoji), { headers: { ETag: 'W/noshort' } }))
|
||||
fetch.head(NO_SHORTCODES, () => new Response(null, { headers: { ETag: 'W/noshort' } }))
|
||||
mockGetAndHead(NO_SHORTCODES, noShortcodeEmoji, { headers: { ETag: 'W/noshort' } })
|
||||
}
|
||||
|
||||
export function mockEmojibaseV5DataSource () {
|
||||
fetch.get(EMOJIBASE_V5, () => new Response(JSON.stringify(emojibaseV5Emoji), { headers: { ETag: 'W/emojibase' } }))
|
||||
fetch.head(EMOJIBASE_V5, () => new Response(null, { headers: { ETag: 'W/emojibase' } }))
|
||||
mockGetAndHead(EMOJIBASE_V5, emojibaseV5Emoji, { headers: { ETag: 'W/emojibase' } })
|
||||
}
|
||||
|
||||
export function mockDataSourceWithArraySkinTones () {
|
||||
const emojis = JSON.parse(JSON.stringify(truncatedEmoji))
|
||||
emojis.push(allEmoji.find(_ => _.annotation === 'people holding hands')) // has two skin tones, one for each person
|
||||
|
||||
fetch
|
||||
.get(WITH_ARRAY_SKIN_TONES, () => (
|
||||
new Response(JSON.stringify(emojis), { headers: { ETag: 'W/noshort' } }))
|
||||
)
|
||||
.head(WITH_ARRAY_SKIN_TONES, () => new Response(null, { headers: { ETag: 'W/noshort' } }))
|
||||
mockGetAndHead(WITH_ARRAY_SKIN_TONES, emojis, { headers: { ETag: 'W/noshort' } })
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue