diff options
Diffstat (limited to 'test')
39 files changed, 54 insertions, 4923 deletions
diff --git a/test/lib/composite.js b/test/lib/composite.js deleted file mode 100644 index 359d364d..00000000 --- a/test/lib/composite.js +++ /dev/null @@ -1,33 +0,0 @@ -import {compositeFrom} from '#composite'; - -export function quickCheckCompositeOutputs(t, dependencies) { - return (step, outputDict) => { - t.same( - Object.keys(step.toDescription().outputs), - Object.keys(outputDict)); - - const composite = compositeFrom({ - compose: false, - steps: [ - step, - - { - dependencies: Object.keys(outputDict), - - // Access all dependencies by their expected keys - - // the composition runner actually provides a proxy - // and is checking that *we* access the dependencies - // we've specified. - compute: dependencies => - Object.fromEntries( - Object.keys(outputDict) - .map(key => [key, dependencies[key]])), - }, - ], - }); - - t.same( - composite.expose.compute(dependencies), - outputDict); - }; -} diff --git a/test/lib/content-function.js b/test/lib/content-function.js deleted file mode 100644 index a46d18c9..00000000 --- a/test/lib/content-function.js +++ /dev/null @@ -1,269 +0,0 @@ -import * as path from 'node:path'; -import {fileURLToPath} from 'node:url'; -import {inspect} from 'node:util'; - -import chroma from 'chroma-js'; - -import {showAggregate} from '#aggregate'; -import {getColors} from '#colors'; -import {quickLoadContentDependencies} from '#content-dependencies'; -import {quickEvaluate} from '#content-function'; -import * as html from '#html'; -import {internalDefaultStringsFile, processLanguageFile} from '#language'; -import {empty} from '#sugar'; -import {generateURLs, thumb, urlSpec} from '#urls'; - -import mock from './generic-mock.js'; - -const __dirname = path.dirname(fileURLToPath(import.meta.url)); - -function cleanURLSpec(reference) { - const prepared = structuredClone(reference); - - for (const spec of Object.values(prepared)) { - if (spec.prefix) { - // Strip out STATIC_VERSION. This updates fairly regularly and we - // don't want it to affect snapshot tests. - spec.prefix = spec.prefix - .replace(/static-\d+[a-z]\d+/i, 'static'); - } - } - - return prepared; -} - -export function testContentFunctions(t, message, fn) { - const urls = generateURLs(cleanURLSpec(urlSpec)); - - t.test(message, async t => { - let loadedContentDependencies; - - const language = await processLanguageFile(internalDefaultStringsFile); - const mocks = []; - - const evaluate = ({ - from = 'localized.home', - contentDependencies = {}, - extraDependencies = {}, - ...opts - }) => { - if (!loadedContentDependencies) { - throw new Error(`Await .load() before performing tests`); - } - - const {to} = urls.from(from); - - return cleanCatchAggregate(() => { - return quickEvaluate({ - ...opts, - contentDependencies: { - ...contentDependencies, - ...loadedContentDependencies, - }, - extraDependencies: { - html, - language, - thumb, - to, - urls, - - pagePath: ['home'], - appendIndexHTML: false, - getColors: c => getColors(c, {chroma}), - - wikiData: { - wikiInfo: {}, - }, - - ...extraDependencies, - }, - }); - }); - }; - - evaluate.load = async (opts) => { - if (loadedContentDependencies) { - throw new Error(`Already loaded!`); - } - - loadedContentDependencies = await asyncCleanCatchAggregate(() => - quickLoadContentDependencies({ - logging: false, - ...opts, - })); - }; - - evaluate.snapshot = (...args) => { - if (!loadedContentDependencies) { - throw new Error(`Await .load() before performing tests`); - } - - const [description, opts] = - (typeof args[0] === 'string' - ? args - : ['output', ...args]); - - let result = evaluate(opts); - - if (opts.multiple) { - result = result.map(item => item.toString()).join('\n'); - } else { - result = result.toString(); - } - - t.matchSnapshot(result, description); - }; - - evaluate.stubTemplate = name => - // Creates a particularly permissable template, allowing any slot values - // to be stored and just outputting the contents of those slots as-are. - _stubTemplate(name, false); - - evaluate.stubContentFunction = name => - // Like stubTemplate, but instead of a template directly, returns - // an object describing a content function - suitable for passing - // into evaluate.mock. - _stubTemplate(name, true); - - const _stubTemplate = (name, mockContentFunction) => { - const inspectNicely = (value, opts = {}) => - inspect(value, { - ...opts, - colors: false, - sort: true, - }); - - const makeTemplate = formatContentFn => - new (class extends html.Template { - #slotValues = {}; - - constructor() { - super({ - content: () => this.#getContent(formatContentFn), - }); - } - - setSlots(slotNamesToValues) { - Object.assign(this.#slotValues, slotNamesToValues); - } - - setSlot(slotName, slotValue) { - this.#slotValues[slotName] = slotValue; - } - - #getContent(formatContentFn) { - const toInspect = - Object.fromEntries( - Object.entries(this.#slotValues) - .filter(([key, value]) => value !== null)); - - const inspected = - inspectNicely(toInspect, { - breakLength: Infinity, - compact: true, - depth: Infinity, - }); - - return formatContentFn(inspected); `${name}: ${inspected}`; - } - }); - - if (mockContentFunction) { - return { - data: (...args) => ({args}), - generate: (data) => - makeTemplate(slots => { - const argsLines = - (empty(data.args) - ? [] - : inspectNicely(data.args, {depth: Infinity}) - .split('\n')); - - return (`[mocked: ${name}` + - - (empty(data.args) - ? `` - : argsLines.length === 1 - ? `\n args: ${argsLines[0]}` - : `\n args: ${argsLines[0]}\n` + - argsLines.slice(1).join('\n').replace(/^/gm, ' ')) + - - (!empty(data.args) - ? `\n ` - : ` - `) + - - (slots - ? `slots: ${slots}]` - : `slots: none]`)); - }), - }; - } else { - return makeTemplate(slots => `${name}: ${slots}`); - } - }; - - evaluate.mock = (...opts) => { - const {value, close} = mock(...opts); - mocks.push({close}); - return value; - }; - - evaluate.mock.transformContent = { - transformContent: { - extraDependencies: ['html'], - data: content => ({content}), - slots: {mode: {type: 'string'}}, - generate: ({content}) => content, - }, - }; - - await fn(t, evaluate); - - if (!empty(mocks)) { - cleanCatchAggregate(() => { - const errors = []; - for (const {close} of mocks) { - try { - close(); - } catch (error) { - errors.push(error); - } - } - if (!empty(errors)) { - throw new AggregateError(errors, `Errors closing mocks`); - } - }); - } - }); -} - -function printAggregate(error) { - if (error instanceof AggregateError) { - const message = showAggregate(error, { - showTraces: true, - print: false, - pathToFileURL: f => path.relative(path.join(__dirname, '../..'), fileURLToPath(f)), - }); - for (const line of message.split('\n')) { - console.error(line); - } - } -} - -function cleanCatchAggregate(fn) { - try { - return fn(); - } catch (error) { - printAggregate(error); - throw error; - } -} - -async function asyncCleanCatchAggregate(fn) { - try { - return await fn(); - } catch (error) { - printAggregate(error); - throw error; - } -} diff --git a/test/lib/generic-mock.js b/test/lib/generic-mock.js deleted file mode 100644 index 28309ab0..00000000 --- a/test/lib/generic-mock.js +++ /dev/null @@ -1,314 +0,0 @@ -import {same} from 'tcompare'; - -import {empty} from '#sugar'; - -export default function mock(callback) { - const mocks = []; - - const track = callback => (...args) => { - const {value, close} = callback(...args); - mocks.push({close}); - return value; - }; - - const mock = { - function: track(mockFunction), - }; - - return { - value: callback(mock), - close: () => { - const errors = []; - for (const mock of mocks) { - try { - mock.close(); - } catch (error) { - errors.push(error); - } - } - if (!empty(errors)) { - throw new AggregateError(errors, `Errors closing sub-mocks`); - } - }, - }; -} - -export function mockFunction(...args) { - let name = '(anonymous)'; - let behavior = null; - - if (args.length === 2) { - if ( - typeof args[0] === 'string' && - typeof args[1] === 'function' - ) { - name = args[0]; - behavior = args[1]; - } else { - throw new TypeError(`Expected name to be a string`); - } - } else if (args.length === 1) { - if (typeof args[0] === 'string') { - name = args[0]; - } else if (typeof args[0] === 'function') { - behavior = args[0]; - } else if (args[0] !== null) { - throw new TypeError(`Expected string (name), function (behavior), both, or null / no arguments`); - } - } else if (args.length > 2) { - throw new TypeError(`Expected string (name), function (behavior), both, or null / no arguments`); - } - - let currentCallDescription = newCallDescription(); - const allCallDescriptions = [currentCallDescription]; - - const topLevelErrors = []; - let runningCallCount = 0; - let limitCallCount = false; - let markedAsOnce = false; - - const fn = (...args) => { - const description = processCall(...args); - return description.behavior(...args); - }; - - fn.behavior = value => { - if (!(value === null || ( - typeof value === 'function' - ))) { - throw new TypeError(`Expected function or null`); - } - - currentCallDescription.behavior = behavior; - currentCallDescription.described = true; - - return fn; - } - - fn.argumentCount = value => { - if (!(value === null || ( - typeof value === 'number' && - value === parseInt(value) && - value >= 0 - ))) { - throw new TypeError(`Expected whole number or null`); - } - - if (currentCallDescription.argsPattern) { - throw new TypeError(`Unexpected .argumentCount() when .args() has been called`); - } - - currentCallDescription.argsPattern = {length: value}; - currentCallDescription.described = true; - - return fn; - }; - - fn.args = (...args) => { - const value = args[0]; - - if (args.length > 1 || !(value === null || Array.isArray(value))) { - throw new TypeError(`Expected one array or null`); - } - - currentCallDescription.argsPattern = Object.fromEntries( - value - .map((v, i) => v === undefined ? false : [i, v]) - .filter(Boolean) - .concat([['length', value.length]])); - - currentCallDescription.described = true; - - return fn; - }; - - fn.neverCalled = (...args) => { - if (!empty(args)) { - throw new TypeError(`Didn't expect any arguments`); - } - - if (allCallDescriptions[0].described) { - throw new TypeError(`Unexpected .neverCalled() when any descriptions provided`); - } - - limitCallCount = true; - allCallDescriptions.splice(0, allCallDescriptions.length); - - currentCallDescription = new Proxy({}, { - set() { - throw new Error(`Unexpected description when .neverCalled() has been called`); - }, - }); - - return fn; - }; - - fn.once = (...args) => { - if (!empty(args)) { - throw new TypeError(`Didn't expect any arguments`); - } - - if (allCallDescriptions.length > 1) { - throw new TypeError(`Unexpected .once() when providing multiple descriptions`); - } - - currentCallDescription.described = true; - limitCallCount = true; - markedAsOnce = true; - - return fn; - }; - - fn.next = (...args) => { - if (!empty(args)) { - throw new TypeError(`Didn't expect any arguments`); - } - - if (markedAsOnce) { - throw new TypeError(`Unexpected .next() when .once() has been called`); - } - - currentCallDescription = newCallDescription(); - allCallDescriptions.push(currentCallDescription); - - limitCallCount = true; - - return fn; - }; - - fn.repeat = times => { - // Note: This function should be called AFTER filling out the - // call description which is being repeated. - - if (!( - typeof times === 'number' && - times === parseInt(times) && - times >= 2 - )) { - throw new TypeError(`Expected whole number of at least 2`); - } - - if (markedAsOnce) { - throw new TypeError(`Unexpected .repeat() when .once() has been called`); - } - - // The current call description is already in the full list, - // so skip the first push. - for (let i = 2; i <= times; i++) { - allCallDescriptions.push(currentCallDescription); - } - - // Prep a new description like when calling .next(). - currentCallDescription = newCallDescription(); - allCallDescriptions.push(currentCallDescription); - - limitCallCount = true; - - return fn; - }; - - return { - value: fn, - close: () => { - const totalCallCount = runningCallCount; - const expectedCallCount = countDescribedCalls(); - - if (limitCallCount && totalCallCount !== expectedCallCount) { - if (expectedCallCount > 1) { - topLevelErrors.push(new Error(`Expected ${expectedCallCount} calls, got ${totalCallCount}`)); - } else if (expectedCallCount === 1) { - topLevelErrors.push(new Error(`Expected 1 call, got ${totalCallCount}`)); - } else { - topLevelErrors.push(new Error(`Expected no calls, got ${totalCallCount}`)); - } - } - - if (topLevelErrors.length) { - throw new AggregateError(topLevelErrors, `Errors in mock ${name}`); - } - }, - }; - - function newCallDescription() { - return { - described: false, - behavior: behavior ?? null, - argumentCount: null, - argsPattern: null, - }; - } - - function processCall(...args) { - const callErrors = []; - - runningCallCount++; - - // No further processing, this indicates the function shouldn't have been - // called at all and there aren't any descriptions to match this call with. - if (empty(allCallDescriptions)) { - return newCallDescription(); - } - - const currentCallNumber = runningCallCount; - const currentDescription = selectCallDescription(currentCallNumber); - - const { - argumentCount, - argsPattern, - } = currentDescription; - - if (argumentCount !== null && args.length !== argumentCount) { - callErrors.push( - new Error(`Argument count mismatch: expected ${argumentCount}, got ${args.length}`)); - } - - if (argsPattern !== null) { - const keysToCheck = Object.keys(argsPattern); - const argsAsObject = Object.fromEntries( - args - .map((v, i) => [i.toString(), v]) - .filter(([i]) => keysToCheck.includes(i)) - .concat([['length', args.length]])); - - const {match, diff} = same(argsAsObject, argsPattern); - if (!match) { - callErrors.push(new Error(`Argument pattern mismatch:\n` + diff)); - } - } - - if (!empty(callErrors)) { - const aggregate = new AggregateError(callErrors, `Errors in call #${currentCallNumber}`); - topLevelErrors.push(aggregate); - } - - return currentDescription; - } - - function selectCallDescription(currentCallNumber) { - if (currentCallNumber > countDescribedCalls()) { - const lastDescription = lastCallDescription(); - if (lastDescription.described) { - return newCallDescription(); - } else { - return lastDescription; - } - } else { - return allCallDescriptions[currentCallNumber - 1]; - } - } - - function countDescribedCalls() { - if (empty(allCallDescriptions)) { - return 0; - } - - return ( - (lastCallDescription().described - ? allCallDescriptions.length - : allCallDescriptions.length - 1)); - } - - function lastCallDescription() { - return allCallDescriptions[allCallDescriptions.length - 1]; - } -} diff --git a/test/lib/index.js b/test/lib/index.js deleted file mode 100644 index 4c9ee23f..00000000 --- a/test/lib/index.js +++ /dev/null @@ -1,7 +0,0 @@ -Error.stackTraceLimit = Infinity; - -export * from './composite.js'; -export * from './content-function.js'; -export * from './generic-mock.js'; -export * from './wiki-data.js'; -export * from './strict-match-error.js'; diff --git a/test/lib/wiki-data.js b/test/lib/wiki-data.js deleted file mode 100644 index 7c3d2147..00000000 --- a/test/lib/wiki-data.js +++ /dev/null @@ -1,153 +0,0 @@ -import CacheableObject from '#cacheable-object'; -import find from '#find'; -import {withEntries} from '#sugar'; -import Thing from '#thing'; -import thingConstructors from '#things'; -import {linkWikiDataArrays} from '#yaml'; - -export function linkAndBindWikiData(wikiData, { - inferAlbumsOwnTrackData = true, -} = {}) { - function customLinkWikiDataArrays(wikiData, options = {}) { - linkWikiDataArrays( - (options.XXX_decacheWikiData - ? withEntries(wikiData, entries => entries - .map(([key, value]) => [key, value.slice()])) - : wikiData)); - } - - customLinkWikiDataArrays(wikiData); - - return { - // Mutate to make the below functions aware of new data objects, or of - // reordering the existing ones. Don't mutate arrays such as trackData - // in-place; assign completely new arrays to this wikiData object instead. - wikiData, - - // Use this after you've mutated wikiData to assign new data arrays. - // It'll automatically relink everything on wikiData so all the objects - // are caught up to date. - linkWikiDataArrays: - customLinkWikiDataArrays - .bind(null, wikiData), - - // Use this if you HAVEN'T mutated wikiData and just need to decache - // indirect dependencies on exposed properties of other data objects. - // - // XXX_decacheWikiData option should be used specifically to mark points - // where you *aren't* replacing any of the arrays under wikiData with - // new values, and are using linkWikiDataArrays to instead "decache" data - // properties which depend on any of them. It's currently not possible for - // a CacheableObject to depend directly on the value of a property exposed - // on some other CacheableObject, so when those values change, you have to - // manually decache before the object will realize its cache isn't valid - // anymore. - // - // The previous implementation for this involved overwriting the relevant - // wikiData properties with null, then replacing it with the original - // array, which effectively cleared a CacheableObject cache. But it isn't - // enough to clear other caches that depend on the identity of wikiData - // arrays, such as withReverseReferenceList, so now it replaces with fresh - // copies of the data arrays instead; the original identities don't get - // reused. - XXX_decacheWikiData: - customLinkWikiDataArrays - .bind(null, wikiData, {XXX_decacheWikiData: true}), - }; -} - -export function stubWikiData() { - return { - albumData: [], - artistData: [], - artTagData: [], - flashData: [], - flashActData: [], - flashSideData: [], - groupData: [], - groupCategoryData: [], - newsData: [], - staticPageData: [], - trackData: [], - trackSectionData: [], - }; -} - -export function stubThing(wikiData, constructor, properties = {}) { - const thing = Reflect.construct(constructor, []); - Object.assign(thing, properties); - - const wikiDataSpec = { - Album: 'albumData', - Artist: 'artistData', - ArtTag: 'artTagData', - Flash: 'flashData', - FlashAct: 'flashActData', - FlashSide: 'flashSideData', - Group: 'groupData', - GroupCategory: 'groupCategoryData', - NewsEntry: 'newsData', - StaticPage: 'staticPageData', - Track: 'trackData', - TrackSection: 'trackSectionData', - }; - - const wikiDataMap = - new Map( - Object.entries(wikiDataSpec) - .map(([thingKey, wikiDataKey]) => [ - thingConstructors[thingKey], - wikiData[wikiDataKey], - ])); - - const wikiDataArray = - wikiDataMap.get(constructor); - - wikiDataArray.push(thing); - - return thing; -} - -export function stubTrackAndAlbum(wikiData, trackDirectory = null, albumDirectory = null) { - const {Track, TrackSection, Album} = thingConstructors; - - const track = - stubThing(wikiData, Track, {directory: trackDirectory}); - - const section = - stubThing(wikiData, TrackSection, {tracks: [track]}); - - const album = - stubThing(wikiData, Album, {directory: albumDirectory, trackSections: [section]}); - - return {track, album, section}; -} - -export function stubArtistAndContribs(wikiData, artistName = `Test Artist`) { - const {Artist} = thingConstructors; - - const artist = - stubThing(wikiData, Artist, {name: artistName}); - - const contribs = - [{artist: artistName, annotation: null}]; - - const badContribs = - [{artist: `Figment of Your Imagination`, annotation: null}]; - - return {artist, contribs, badContribs}; -} - -export function stubFlashAndAct(wikiData, flashDirectory = null) { - const {Flash, FlashAct} = thingConstructors; - - const flash = - stubThing(wikiData, Flash, {directory: flashDirectory}); - - const flashAct = - stubThing(wikiData, FlashAct, { - flashes: [Thing.getReference(flash)], - }); - - return {flash, flashAct}; -} diff --git a/test/snapshot/generateAlbumAdditionalFilesList.js b/test/snapshot/generateAlbumAdditionalFilesList.js deleted file mode 100644 index c25e5682..00000000 --- a/test/snapshot/generateAlbumAdditionalFilesList.js +++ /dev/null @@ -1,84 +0,0 @@ -import t from 'tap'; - -import {testContentFunctions} from '#test-lib'; -import thingConstructors from '#things'; - -const {Album} = thingConstructors; - -testContentFunctions(t, 'generateAlbumAdditionalFilesList (snapshot)', async (t, evaluate) => { - const sizeMap = { - 'sburbwp_1280x1024.jpg': 2500, - 'sburbwp_1440x900.jpg': null, - 'sburbwp_1920x1080.jpg': null, - 'Internet Explorer.gif': 1, - 'Homestuck_Vol4_alt1.jpg': 1234567, - 'Homestuck_Vol4_alt2.jpg': 1234567, - 'Homestuck_Vol4_alt3.jpg': 1234567, - }; - - const extraDependencies = { - getSizeOfAdditionalFile: file => - Object.entries(sizeMap) - .find(key => file.includes(key)) - ?.at(1) ?? null, - }; - - await evaluate.load({ - mock: { - image: evaluate.stubContentFunction('image'), - }, - }); - - const album = new Album(); - album.directory = 'exciting-album'; - - evaluate.snapshot('no additional files', { - extraDependencies, - name: 'generateAlbumAdditionalFilesList', - args: [album, []], - }); - - try { - evaluate.snapshot('basic behavior', { - extraDependencies, - name: 'generateAlbumAdditionalFilesList', - args: [ - album, - [ - { - title: 'SBURB Wallpaper', - files: [ - 'sburbwp_1280x1024.jpg', - 'sburbwp_1440x900.jpg', - 'sburbwp_1920x1080.jpg', - ], - }, - { - title: 'Fake Section', - description: 'No sizes for these files', - files: [ - 'oops.mp3', - 'Internet Explorer.gif', - 'daisy.mp3', - ], - }, - { - title: `Empty Section`, - description: `These files haven't been made available.`, - }, - { - title: 'Alternate Covers', - description: 'This is just an example description.', - files: [ - 'Homestuck_Vol4_alt1.jpg', - 'Homestuck_Vol4_alt2.jpg', - 'Homestuck_Vol4_alt3.jpg', - ], - }, - ], - ], - }); - } catch (error) { - console.log(error); - } -}); diff --git a/test/snapshot/generateAlbumBanner.js b/test/snapshot/generateAlbumBanner.js deleted file mode 100644 index 8e63308f..00000000 --- a/test/snapshot/generateAlbumBanner.js +++ /dev/null @@ -1,34 +0,0 @@ -import t from 'tap'; -import {testContentFunctions} from '#test-lib'; - -testContentFunctions(t, 'generateAlbumBanner (snapshot)', async (t, evaluate) => { - await evaluate.load(); - - evaluate.snapshot('basic behavior', { - name: 'generateAlbumBanner', - args: [{ - directory: 'cool-album', - hasBannerArt: true, - bannerDimensions: [800, 200], - bannerFileExtension: 'png', - }], - }); - - evaluate.snapshot('no dimensions', { - name: 'generateAlbumBanner', - args: [{ - directory: 'cool-album', - hasBannerArt: true, - bannerDimensions: null, - bannerFileExtension: 'png', - }], - }); - - evaluate.snapshot('no banner', { - name: 'generateAlbumBanner', - args: [{ - directory: 'cool-album', - hasBannerArt: false, - }], - }); -}); diff --git a/test/snapshot/generateAlbumReleaseInfo.js b/test/snapshot/generateAlbumReleaseInfo.js deleted file mode 100644 index f41e502d..00000000 --- a/test/snapshot/generateAlbumReleaseInfo.js +++ /dev/null @@ -1,74 +0,0 @@ -import t from 'tap'; -import {testContentFunctions} from '#test-lib'; - -testContentFunctions(t, 'generateAlbumReleaseInfo (snapshot)', async (t, evaluate) => { - await evaluate.load(); - - evaluate.snapshot('basic behavior', { - name: 'generateAlbumReleaseInfo', - args: [{ - artistContribs: [ - {artist: {name: 'Toby Fox', directory: 'toby-fox', urls: []}, annotation: 'music probably'}, - {artist: {name: 'Tensei', directory: 'tensei', urls: ['https://tenseimusic.bandcamp.com/']}, annotation: 'hot jams'}, - ], - - coverArtistContribs: [ - {artist: {name: 'Hanni Brosh', directory: 'hb', urls: []}, annotation: null}, - ], - - wallpaperArtistContribs: [ - {artist: {name: 'Hanni Brosh', directory: 'hb', urls: []}, annotation: null}, - {artist: {name: 'Niklink', directory: 'niklink', urls: []}, annotation: 'edits'}, - ], - - bannerArtistContribs: [ - {artist: {name: 'Hanni Brosh', directory: 'hb', urls: []}, annotation: null}, - {artist: {name: 'Niklink', directory: 'niklink', urls: []}, annotation: 'edits'}, - ], - - name: 'AlterniaBound', - date: new Date('March 14, 2011'), - coverArtDate: new Date('April 1, 1991'), - urls: [ - 'https://homestuck.bandcamp.com/album/alterniabound-with-alternia', - 'https://www.youtube.com/playlist?list=PLnVpmehyaOFZWO9QOZmD6A3TIK0wZ6xE2', - 'https://www.youtube.com/watch?v=HO5V2uogkYc', - ], - - tracks: [{duration: 253}, {duration: 372}], - }], - }); - - const sparse = { - artistContribs: [], - coverArtistContribs: [], - wallpaperArtistContribs: [], - bannerArtistContribs: [], - - name: 'Suspicious Album', - urls: [], - tracks: [], - }; - - evaluate.snapshot('reduced details', { - name: 'generateAlbumReleaseInfo', - args: [sparse], - }); - - evaluate.snapshot('URLs only', { - name: 'generateAlbumReleaseInfo', - args: [{ - ...sparse, - urls: ['https://homestuck.bandcamp.com/foo', 'https://soundcloud.com/bar'], - }], - }); - - evaluate.snapshot('equal cover art date', { - name: 'generateAlbumReleaseInfo', - args: [{ - ...sparse, - date: new Date('2020-04-13'), - coverArtDate: new Date('2020-04-13'), - }], - }); -}); diff --git a/test/snapshot/generateAlbumSecondaryNav.js b/test/snapshot/generateAlbumSecondaryNav.js deleted file mode 100644 index 2495bc4a..00000000 --- a/test/snapshot/generateAlbumSecondaryNav.js +++ /dev/null @@ -1,90 +0,0 @@ -import t from 'tap'; -import {testContentFunctions} from '#test-lib'; - -testContentFunctions(t, 'generateAlbumSecondaryNav (snapshot)', async (t, evaluate) => { - await evaluate.load(); - - let album, anotherAlbum, group1, group2; - - group1 = { - name: 'VCG', - directory: 'vcg', - color: '#abcdef', - serieses: [], - }; - - group2 = { - name: 'Bepis', - directory: 'bepis', - color: '#123456', - serieses: [], - }; - - album = { - name: 'Album', - directory: 'album', - date: new Date('2010-04-13'), - groups: [group1, group2], - }; - - anotherAlbum = { - name: 'Last', - directory: 'last', - date: new Date('2010-06-12'), - }; - - group1.albums = [ - { - name: 'First', - directory: 'first', - date: new Date('2010-04-10'), - }, - album, - anotherAlbum, - ]; - - group1.serieses = [ - { - name: 'Series', - albums: [album, anotherAlbum], - group: group1, - }, - ]; - - group2.albums = [ - album, - { - name: 'Second', - directory: 'second', - date: new Date('2011-04-13'), - }, - ]; - - evaluate.snapshot('basic behavior, mode: album', { - name: 'generateAlbumSecondaryNav', - args: [album], - slots: {mode: 'album'}, - }); - - evaluate.snapshot('basic behavior, mode: track', { - name: 'generateAlbumSecondaryNav', - args: [album], - slots: {mode: 'track'}, - }); - - album = { - date: null, - groups: [group1, group2], - }; - - group1.albums = [ - ...group1.albums, - album, - ]; - - evaluate.snapshot('dateless album in mixed group', { - name: 'generateAlbumSecondaryNav', - args: [album], - slots: {mode: 'album'}, - }); -}); diff --git a/test/snapshot/generateAlbumSidebarGroupBox.js b/test/snapshot/generateAlbumSidebarGroupBox.js deleted file mode 100644 index f920bd96..00000000 --- a/test/snapshot/generateAlbumSidebarGroupBox.js +++ /dev/null @@ -1,57 +0,0 @@ -import t from 'tap'; -import {testContentFunctions} from '#test-lib'; - -testContentFunctions(t, 'generateAlbumSidebarGroupBox (snapshot)', async (t, evaluate) => { - await evaluate.load({ - mock: { - ...evaluate.mock.transformContent, - }, - }); - - let album, group; - - album = { - name: 'Middle', - directory: 'middle', - date: new Date('2010-04-13'), - }; - - group = { - name: 'VCG', - directory: 'vcg', - descriptionShort: 'Very cool group.', - urls: ['https://vcg.bandcamp.com/', 'https://youtube.com/@vcg'], - albums: [ - {name: 'First', directory: 'first', date: new Date('2010-04-10')}, - album, - {name: 'Last', directory: 'last', date: new Date('2010-06-12')}, - ], - }; - - evaluate.snapshot('basic behavior, mode: album', { - name: 'generateAlbumSidebarGroupBox', - args: [album, group], - slots: {mode: 'album'}, - }); - - evaluate.snapshot('basic behavior, mode: track', { - name: 'generateAlbumSidebarGroupBox', - args: [album, group], - slots: {mode: 'track'}, - }); - - album = { - date: null, - }; - - group.albums = [ - ...group.albums, - album, - ]; - - evaluate.snapshot('dateless album in mixed group', { - name: 'generateAlbumSidebarGroupBox', - args: [album, group], - slots: {mode: 'album'}, - }); -}); diff --git a/test/snapshot/generateAlbumTrackList.js b/test/snapshot/generateAlbumTrackList.js deleted file mode 100644 index e09f1c4a..00000000 --- a/test/snapshot/generateAlbumTrackList.js +++ /dev/null @@ -1,123 +0,0 @@ -import t from 'tap'; -import {testContentFunctions} from '#test-lib'; - -testContentFunctions(t, 'generateAlbumTrackList (snapshot)', async (t, evaluate) => { - await evaluate.load({ - mock: { - generateAlbumTrackListMissingDuration: - evaluate.stubContentFunction('generateAlbumTrackListMissingDuration'), - - image: - evaluate.stubContentFunction('image'), - }, - }); - - const contribs1 = [ - {artist: {name: 'Apricot', directory: 'apricot', urls: []}}, - ]; - - const contribs2 = [ - {artist: {name: 'Apricot', directory: 'apricot', urls: []}}, - {artist: {name: 'Peach', directory: 'peach', urls: ['https://peach.bandcamp.com/']}}, - {artist: {name: 'Cerise', directory: 'cerise', urls: []}}, - ]; - - const color1 = '#fb07ff'; - const color2 = '#ea2e83'; - - const tracks = [ - {name: 'Track 1', directory: 't1', duration: 20, artistContribs: contribs1, color: color1}, - {name: 'Track 2', directory: 't2', duration: 0, artistContribs: contribs1, color: color1}, - {name: 'Track 3', directory: 't3', duration: 40, artistContribs: contribs1, color: color1}, - {name: 'Track 4', directory: 't4', duration: 0, artistContribs: contribs2, color: color2}, - ]; - - const albumWithTrackSections = { - color: color1, - artistContribs: contribs1, - trackSections: [ - {name: 'First section', tracks: tracks.slice(0, 3)}, - {name: 'Second section', tracks: tracks.slice(3)}, - ], - tracks, - }; - - const albumWithoutTrackSections = { - color: color1, - artistContribs: contribs1, - trackSections: [{isDefaultTrackSection: true, tracks}], - tracks, - }; - - const albumWithTrackSectionDescriptions = { - color: color1, - artistContribs: contribs1, - trackSections: [ - {name: 'First section', tracks: tracks.slice(0, 3), description: `Why yes!`}, - {name: 'Second section', tracks: tracks.slice(3), description: `How *contentful,* this is.`}, - ], - tracks, - }; - - const albumWithNoDuration = { - color: color1, - artistContribs: contribs1, - trackSections: [{isDefaultTrackSection: true, tracks: [tracks[1], tracks[3]]}], - tracks: [tracks[1], tracks[3]], - }; - - evaluate.snapshot(`basic behavior, with track sections`, { - name: 'generateAlbumTrackList', - args: [albumWithTrackSections], - }); - - evaluate.snapshot(`basic behavior, default track section`, { - name: 'generateAlbumTrackList', - args: [albumWithoutTrackSections], - }); - - evaluate.snapshot(`basic behavior, with descriptions`, { - name: 'generateAlbumTrackList', - args: [albumWithTrackSectionDescriptions], - }); - - evaluate.snapshot(`collapseDurationScope: never`, { - name: 'generateAlbumTrackList', - slots: {collapseDurationScope: 'never'}, - multiple: [ - {args: [albumWithTrackSections]}, - {args: [albumWithoutTrackSections]}, - {args: [albumWithNoDuration]}, - ], - }); - - evaluate.snapshot(`collapseDurationScope: track`, { - name: 'generateAlbumTrackList', - slots: {collapseDurationScope: 'track'}, - multiple: [ - {args: [albumWithTrackSections]}, - {args: [albumWithoutTrackSections]}, - {args: [albumWithNoDuration]}, - ], - }); - - evaluate.snapshot(`collapseDurationScope: section`, { - name: 'generateAlbumTrackList', - slots: {collapseDurationScope: 'section'}, - multiple: [ - {args: [albumWithTrackSections]}, - {args: [albumWithoutTrackSections]}, - {args: [albumWithNoDuration]}, - ], - }); - - evaluate.snapshot(`collapseDurationScope: album`, { - name: 'generateAlbumTrackList', - slots: {collapseDurationScope: 'album'}, - multiple: [ - {args: [albumWithTrackSections]}, - {args: [albumWithoutTrackSections]}, - {args: [albumWithNoDuration]}, - ], - }); -}); diff --git a/test/snapshot/generateBanner.js b/test/snapshot/generateBanner.js deleted file mode 100644 index ab57c3cc..00000000 --- a/test/snapshot/generateBanner.js +++ /dev/null @@ -1,22 +0,0 @@ -import t from 'tap'; -import {testContentFunctions} from '#test-lib'; - -testContentFunctions(t, 'generateBanner (snapshot)', async (t, evaluate) => { - await evaluate.load(); - - evaluate.snapshot('basic behavior', { - name: 'generateBanner', - slots: { - path: ['media.albumBanner', 'cool-album', 'png'], - alt: 'Very cool banner art.', - dimensions: [800, 200], - }, - }); - - evaluate.snapshot('no dimensions', { - name: 'generateBanner', - slots: { - path: ['media.albumBanner', 'cool-album', 'png'], - }, - }); -}); diff --git a/test/snapshot/generateTrackReleaseInfo.js b/test/snapshot/generateTrackReleaseInfo.js deleted file mode 100644 index 931377c8..00000000 --- a/test/snapshot/generateTrackReleaseInfo.js +++ /dev/null @@ -1,51 +0,0 @@ -import t from 'tap'; -import {testContentFunctions} from '#test-lib'; - -testContentFunctions(t, 'generateTrackReleaseInfo (snapshot)', async (t, evaluate) => { - await evaluate.load(); - - const artistContribs = [{artist: {name: 'Toby Fox', directory: 'toby-fox', urls: []}, annotation: null}]; - const coverArtistContribs = [{artist: {name: 'Alpaca', directory: 'alpaca', urls: []}, annotation: '🔥'}]; - - evaluate.snapshot('basic behavior', { - name: 'generateTrackReleaseInfo', - args: [{ - artistContribs, - name: 'An Apple Disaster!!', - date: new Date('2011-11-30'), - duration: 58, - urls: ['https://soundcloud.com/foo', 'https://youtube.com/watch?v=bar'], - }], - }); - - const sparse = { - artistContribs, - name: 'Suspicious Track', - date: null, - duration: null, - urls: [], - }; - - evaluate.snapshot('reduced details', { - name: 'generateTrackReleaseInfo', - args: [sparse], - }); - - evaluate.snapshot('cover artist contribs, non-unique', { - name: 'generateTrackReleaseInfo', - args: [{ - ...sparse, - coverArtistContribs, - hasUniqueCoverArt: false, - }], - }); - - evaluate.snapshot('cover artist contribs, unique', { - name: 'generateTrackReleaseInfo', - args: [{ - ...sparse, - coverArtistContribs, - hasUniqueCoverArt: true, - }], - }); -}); diff --git a/test/snapshot/image.js b/test/snapshot/image.js deleted file mode 100644 index 1985211f..00000000 --- a/test/snapshot/image.js +++ /dev/null @@ -1,132 +0,0 @@ -import t from 'tap'; -import {testContentFunctions} from '#test-lib'; - -testContentFunctions(t, 'image (snapshot)', async (t, evaluate) => { - await evaluate.load(); - - const quickSnapshot = (message, {extraDependencies, ...opts}) => - evaluate.snapshot(message, { - name: 'image', - extraDependencies: { - checkIfImagePathHasCachedThumbnails: path => !path.endsWith('.gif'), - getSizeOfImagePath: () => 0, - getDimensionsOfImagePath: () => [600, 600], - getThumbnailEqualOrSmaller: () => 'medium', - getThumbnailsAvailableForDimensions: () => - [['large', 800], ['medium', 400], ['small', 250]], - missingImagePaths: ['album-art/missing/cover.png'], - ...extraDependencies, - }, - ...opts, - }); - - quickSnapshot('source via path', { - slots: { - path: ['media.albumCover', 'beyond-canon', 'png'], - }, - }); - - quickSnapshot('source via src', { - slots: { - src: 'https://example.com/bananas.gif', - }, - }); - - quickSnapshot('source missing', { - slots: { - missingSourceContent: 'Example of missing source message.', - }, - }); - - quickSnapshot('dimensions', { - slots: { - src: 'foobar', - dimensions: [600, 400], - }, - }); - - quickSnapshot('square', { - slots: { - src: 'foobar', - square: true, - }, - }); - - quickSnapshot('dimensions with square', { - slots: { - src: 'foobar', - dimensions: [600, 400], - square: true, - }, - }); - - quickSnapshot('lazy with square', { - slots: { - src: 'foobar', - lazy: true, - square: true, - }, - }); - - quickSnapshot('link with file size', { - extraDependencies: { - getSizeOfImagePath: () => 10 ** 6, - }, - slots: { - path: ['media.albumCover', 'pingas', 'png'], - link: true, - }, - }); - - quickSnapshot('content warnings via tags', { - args: [ - [ - {name: 'Dirk Strider', directory: 'dirk'}, - {name: 'too cool for school', isContentWarning: true}, - ], - ], - slots: { - path: ['media.albumCover', 'beyond-canon', 'png'], - }, - }); - - evaluate.snapshot('thumbnail details', { - name: 'image', - extraDependencies: { - checkIfImagePathHasCachedThumbnails: () => true, - getSizeOfImagePath: () => 0, - getDimensionsOfImagePath: () => [900, 1200], - getThumbnailsAvailableForDimensions: () => - [['voluminous', 1200], ['middling', 900], ['petite', 20]], - getThumbnailEqualOrSmaller: () => 'voluminous', - missingImagePaths: [], - }, - slots: { - thumb: 'gargantuan', - path: ['media.albumCover', 'beyond-canon', 'png'], - }, - }); - - quickSnapshot('thumb requested but source is gif', { - slots: { - thumb: 'medium', - path: ['media.flashArt', '5426', 'gif'], - }, - }); - - quickSnapshot('missing image path', { - slots: { - thumb: 'medium', - path: ['media.albumCover', 'missing', 'png'], - link: true, - }, - }); - - quickSnapshot('missing image path w/ missingSourceContent', { - slots: { - thumb: 'medium', - path: ['media.albumCover', 'missing', 'png'], - missingSourceContent: `Cover's missing, whoops`, - }, - }); -}); diff --git a/test/snapshot/linkArtist.js b/test/snapshot/linkArtist.js deleted file mode 100644 index 7b2114b5..00000000 --- a/test/snapshot/linkArtist.js +++ /dev/null @@ -1,30 +0,0 @@ -import t from 'tap'; -import {testContentFunctions} from '#test-lib'; - -testContentFunctions(t, 'linkArtist (snapshot)', async (t, evaluate) => { - await evaluate.load(); - - evaluate.snapshot('basic behavior', { - name: 'linkArtist', - args: [ - { - name: `Toby Fox`, - directory: `toby-fox`, - } - ], - }); - - evaluate.snapshot('prefer short name', { - name: 'linkArtist', - args: [ - { - name: 'ICCTTCMDMIROTMCWMWFTPFTDDOTARHPOESWGBTWEATFCWSEBTSSFOFG', - nameShort: '55gore', - directory: '55gore', - }, - ], - slots: { - preferShortName: true, - }, - }); -}); diff --git a/test/snapshot/linkContribution.js b/test/snapshot/linkContribution.js deleted file mode 100644 index 47ef1503..00000000 --- a/test/snapshot/linkContribution.js +++ /dev/null @@ -1,73 +0,0 @@ -import t from 'tap'; -import {testContentFunctions} from '#test-lib'; - -testContentFunctions(t, 'linkContribution (snapshot)', async (t, evaluate) => { - await evaluate.load(); - - const quickSnapshot = (message, slots) => - evaluate.snapshot(message, { - name: 'linkContribution', - multiple: [ - {args: [ - {artist: { - name: 'Clark Powell', - directory: 'clark-powell', - urls: ['https://soundcloud.com/plazmataz'], - }, annotation: null}, - ]}, - {args: [ - {artist: { - name: 'Grounder & Scratch', - directory: 'the-big-baddies', - urls: [], - }, annotation: 'Snooping'}, - ]}, - {args: [ - {artist: { - name: 'Toby Fox', - directory: 'toby-fox', - urls: ['https://tobyfox.bandcamp.com/', 'https://toby.fox/'], - }, annotation: 'Arrangement'}, - ]}, - ], - slots, - }); - - quickSnapshot('showAnnotation & showExternalLinks', { - showAnnotation: true, - showExternalLinks: true, - }); - - quickSnapshot('only showAnnotation', { - showAnnotation: true, - }); - - quickSnapshot('only showExternalLinks', { - showExternalLinks: true, - }); - - quickSnapshot('no accents', {}); - - evaluate.snapshot('loads of links', { - name: 'linkContribution', - args: [ - {artist: {name: 'Lorem Ipsum Lover', directory: 'lorem-ipsum-lover', urls: [ - 'https://loremipsum.io', - 'https://loremipsum.io/generator/', - 'https://loremipsum.io/#meaning', - 'https://loremipsum.io/#usage-and-examples', - 'https://loremipsum.io/#controversy', - 'https://loremipsum.io/#when-to-use-lorem-ipsum', - 'https://loremipsum.io/#lorem-ipsum-all-the-things', - 'https://loremipsum.io/#original-source', - ]}, annotation: null}, - ], - slots: {showExternalLinks: true}, - }); - - quickSnapshot('no preventWrapping', { - showAnnotation: true, - showExternalLinks: true, - preventWrapping: false, - }); -}); diff --git a/test/snapshot/linkExternal.js b/test/snapshot/linkExternal.js deleted file mode 100644 index 90c98f4b..00000000 --- a/test/snapshot/linkExternal.js +++ /dev/null @@ -1,225 +0,0 @@ -import t from 'tap'; -import {testContentFunctions} from '#test-lib'; - -testContentFunctions(t, 'linkExternal (snapshot)', async (t, evaluate) => { - await evaluate.load(); - - evaluate.snapshot('unknown domain (arbitrary world wide web path)', { - name: 'linkExternal', - args: ['https://snoo.ping.as/usual/i/see/'], - }); - - const urlsToArgs = urls => - urls.map(url => ({args: [url]})); - - const quickSnapshot = (message, urls, slots) => - evaluate.snapshot(message, { - name: 'linkExternal', - slots, - multiple: urlsToArgs(urls), - }); - - const quickSnapshotAllStyles = (context, urls) => { - for (const style of ['platform', 'handle']) { - const message = `context: ${context}, style: ${style}`; - quickSnapshot(message, urls, {context, style}); - } - }; - - // Try to comprehensively test every regular expression - // (in `match` and extractions like `handle` or `details`). - - // Try to *also* represent a reasonable variety of what kinds - // of URLs appear throughout the wiki. (This should serve to - // identify areas which #external-links is expected to - // accommodate, regardless whether or not there is special - // attention given in the actual descriptors.) - - // For normal custom-domain matches (e.g. Mastodon), - // it's OK to just test one custom domain in the list. - - // Generally match the sorting order in externalLinkSpec, - // so corresponding and missing test cases are easy to locate. - - quickSnapshotAllStyles('generic', [ - // platform: appleMusic - 'https://music.apple.com/us/artist/system-of-a-down/462715', - - // platform: artstation - 'https://www.artstation.com/eevaningtea', - 'https://witnesstheabsurd.artstation.com/', - - // platform: bandcamp - 'https://music.solatrus.com/', - 'https://homestuck.bandcamp.com/', - - // platform: bluesky - 'https://bsky.app/profile/jacobtheloofah.bsky.social', - - // platform: carrd - 'https://aliceflare.carrd.co', - 'https://bigchaslappa.carrd.co/', - - // platform: cohost - 'https://cohost.org/cosmoptera', - - // platform: deconreconstruction.music - 'https://music.deconreconstruction.com/albums/catch-322', - 'https://music.deconreconstruction.com/albums/catch-322?track=arcjecs-theme', - - // platform: deconreconstruction - 'https://www.deconreconstruction.com/', - - // platform: deviantart - 'https://culdhira.deviantart.com', - 'https://www.deviantart.com/chesswanderlust-sama', - 'https://www.deviantart.com/shilloshilloh/art/Homestuck-Jake-English-268874606', - - // platform: facebook - 'https://www.facebook.com/DoomedCloud/', - 'https://www.facebook.com/pages/WoodenToaster/280642235307371', - 'https://www.facebook.com/Svixy/posts/400018786702633', - - // platform: fandom.mspaintadventures - 'https://mspaintadventures.fandom.com/wiki/Draconian_Dignitary', - 'https://mspaintadventures.fandom.com/wiki/', - 'https://mspaintadventures.fandom.com/', - - // platform: fandom - 'https://community.fandom.com/', - 'https://community.fandom.com/wiki/', - 'https://community.fandom.com/wiki/Community_Central', - - // platform: gamebanana - 'https://gamebanana.com/members/2028092', - 'https://gamebanana.com/mods/459476', - - // platform: homestuck - 'https://homestuck.com/', - - // platform: hsmusic.archive - 'https://hsmusic.wiki/media/misc/archive/Firefly%20Cloud%20Remix.mp3', - - // platform: hsmusic - 'https://hsmusic.wiki/feedback/', - - // platform: internetArchive - 'https://archive.org/details/a-life-well-lived', - 'https://archive.org/details/VastError_Volume1/11+Renaissance.mp3', - - // platform: instagram - 'https://instagram.com/bass.and.noises', - 'https://www.instagram.com/levc_egm/', - - // platform: itch - 'https://tuyoki.itch.io/', - 'https://itch.io/profile/bravelittletoreador', - - // platform: ko-fi - 'https://ko-fi.com/gnaach', - - // platform: linktree - 'https://linktr.ee/bbpanzu', - - // platform: mastodon - 'https://types.pl/', - - // platform: mspfa - 'https://canwc.mspfa.com/', - 'https://mspfa.com/?s=12003&p=1045', - 'https://mspfa.com/user/?u=103334508819793669241', - - // platform: neocities - 'https://wodaro.neocities.org', - 'https://neomints.neocities.org/', - - // platform: newgrounds - 'https://buzinkai.newgrounds.com/', - 'https://www.newgrounds.com/audio/listen/1256058', - - // platform: patreon - 'https://www.patreon.com/CecilyRenns', - - // platform: poetryFoundation - 'https://www.poetryfoundation.org/poets/christina-rossetti', - 'https://www.poetryfoundation.org/poems/45000/remember-56d224509b7ae', - - // platform: soundcloud - 'https://soundcloud.com/plazmataz', - 'https://soundcloud.com/worthikids/1-i-accidentally-broke-my', - - // platform: spotify - 'https://open.spotify.com/artist/63SNNpNOicDzG3LY82G4q3', - 'https://open.spotify.com/album/0iHvPD8rM3hQa0qeVtPQ3t', - 'https://open.spotify.com/track/6YEGQH32aAXb9vQQbBrPlw', - - // platform: tiktok - 'https://www.tiktok.com/@richaadeb', - - // platform: toyhouse - 'https://toyhou.se/ghastaboo', - - // platform: tumblr - 'https://aeritus.tumblr.com/', - 'https://vol5anthology.tumblr.com/post/159528808107/hey-everyone-its-413-and-that-means-we-have', - 'https://www.tumblr.com/electricwestern', - 'https://www.tumblr.com/spellmynamewithabang/142767566733/happy-413-this-is-the-first-time-anyones-heard', - - // platform: twitch - 'https://www.twitch.tv/ajhebard', - 'https://www.twitch.tv/vargskelethor/', - - // platform: twitter - 'https://twitter.com/awkwarddoesart', - 'https://twitter.com/purenonsens/', - 'https://twitter.com/circlejourney/status/1202265927183548416', - - // platform: waybackMachine - 'https://web.archive.org/web/20120405160556/https://homestuck.bandcamp.com/album/colours-and-mayhem-universe-a', - 'https://web.archive.org/web/20160807111207/http://griffinspacejam.com:80/', - - // platform: wikipedia - 'https://en.wikipedia.org/wiki/Haydn_Quartet_(vocal_ensemble)', - - // platform: youtube - 'https://youtube.com/@bani-chan8949', - 'https://www.youtube.com/@Razzie16', - 'https://www.youtube.com/channel/UCQXfvlKkpbOqEz4BepHqK7g', - 'https://www.youtube.com/watch?v=6ekVnZm29kw', - 'https://youtu.be/WBkC038wSio', - 'https://www.youtube.com/playlist?list=PLy5UGIMKOXpONMExgI7lVYFwQa54QFp_H', - ]); - - quickSnapshotAllStyles('album', [ - 'https://youtu.be/abc', - 'https://youtube.com/watch?v=abc', - 'https://youtube.com/Playlist?list=kweh', - ]); - - quickSnapshotAllStyles('albumNoTracks', [ - 'https://youtu.be/abc', - 'https://youtube.com/watch?v=abc', - 'https://youtube.com/Playlist?list=kweh', - ]); - - quickSnapshotAllStyles('albumOneTrack', [ - 'https://youtu.be/abc', - 'https://youtube.com/watch?v=abc', - 'https://youtube.com/Playlist?list=kweh', - ]); - - quickSnapshotAllStyles('albumMultipleTracks', [ - 'https://youtu.be/abc', - 'https://youtube.com/watch?v=abc', - 'https://youtube.com/Playlist?list=kweh', - ]); - - quickSnapshotAllStyles('flash', [ - 'https://www.bgreco.net/hsflash/002238.html', - 'https://homestuck.com/story/1234', - 'https://homestuck.com/story/pony', - 'https://www.youtube.com/watch?v=wKgOp3Kg2wI', - 'https://youtu.be/IOcvkkklWmY', - 'https://some.external.site/foo/bar/', - ]); -}); diff --git a/test/snapshot/linkTemplate.js b/test/snapshot/linkTemplate.js deleted file mode 100644 index 300065e2..00000000 --- a/test/snapshot/linkTemplate.js +++ /dev/null @@ -1,63 +0,0 @@ -import t from 'tap'; -import * as html from '#html'; -import {testContentFunctions} from '#test-lib'; - -testContentFunctions(t, 'linkTemplate (snapshot)', async (t, evaluate) => { - await evaluate.load(); - - evaluate.snapshot('fill many slots', { - name: 'linkTemplate', - - slots: { - 'href': 'https://hsmusic.wiki/media/cool file.pdf', - 'hash': 'fooey', - 'attributes': {class: 'dog', id: 'cat1'}, - 'content': 'My Cool Link', - }, - }); - - evaluate.snapshot('fill path slot & provide appendIndexHTML', { - name: 'linkTemplate', - - extraDependencies: { - to: (...path) => '/c*lzone/' + path.join('/') + '/', - appendIndexHTML: true, - }, - - slots: { - path: ['myCoolPath', 'ham', 'pineapple', 'tomato'], - content: 'delish', - }, - }); - - evaluate.snapshot('special characters in path argument', { - name: 'linkTemplate', - slots: { - path: [ - 'media.albumAdditionalFile', - 'homestuck-vol-1', - 'Showtime (Piano Refrain) - #xXxAwesomeSheetMusick?rxXx#.pdf', - ], - content: `Damn, that's some good sheet music`, - }, - }); - - evaluate.snapshot('missing content', { - name: 'linkTemplate', - slots: {href: 'banana'}, - }); - - evaluate.snapshot('link in content', { - name: 'linkTemplate', - slots: { - hash: 'the-more-ye-know', - content: [ - `Oh geez oh heck`, - html.tag('a', {href: 'dogs'}, `There's a link in here!!`), - `But here's <b>a normal tag.</b>`, - html.tag('div', `Gotta keep them normal tags.`), - html.tag('div', `But not... <a href="#">NESTED LINKS, OOO.</a>`), - ], - }, - }); -}); diff --git a/test/snapshot/linkThing.js b/test/snapshot/linkThing.js deleted file mode 100644 index 9b5cff33..00000000 --- a/test/snapshot/linkThing.js +++ /dev/null @@ -1,94 +0,0 @@ -import t from 'tap'; -import * as html from '#html'; -import {testContentFunctions} from '#test-lib'; - -testContentFunctions(t, 'linkThing (snapshot)', async (t, evaluate) => { - await evaluate.load(); - - const quickSnapshot = (message, oneOrMultiple) => - evaluate.snapshot(message, - (Array.isArray(oneOrMultiple) - ? {name: 'linkThing', multiple: oneOrMultiple} - : {name: 'linkThing', ...oneOrMultiple})); - - quickSnapshot('basic behavior', { - args: ['localized.track', { - directory: 'foo', - color: '#abcdef', - name: `Cool track!`, - }], - }); - - quickSnapshot('preferShortName', { - args: ['localized.artTagGallery', { - directory: 'five-oceanfalls', - name: 'Five (Oceanfalls)', - nameShort: 'Five', - }], - slots: {preferShortName: true}, - }); - - quickSnapshot('tooltip & content', { - args: ['localized.album', { - directory: 'beyond-canon', - name: 'Beyond Canon', - nameShort: 'BC', - }], - multiple: [ - {slots: {tooltipStyle: 'none'}}, - {slots: {tooltipStyle: 'browser'}}, - {slots: {tooltipStyle: 'browser', content: 'Next'}}, - {slots: {tooltipStyle: 'auto'}}, - {slots: {tooltipStyle: 'auto', preferShortName: true}}, - {slots: {tooltipStyle: 'auto', preferShortName: true, content: 'Next'}}, - {slots: {tooltipStyle: 'auto', content: 'Next'}}, - {slots: {tooltipStyle: 'wiki'}}, - {slots: {tooltipStyle: 'wiki', content: 'Next'}}, - {slots: {content: 'Banana'}}, - ], - }); - - quickSnapshot('color', { - args: ['localized.track', { - directory: 'showtime-piano-refrain', - name: 'Showtime (Piano Refrain)', - color: '#38f43d', - }], - multiple: [ - {slots: {color: false}}, - {slots: {color: true}}, - {slots: {color: '#aaccff'}}, - {slots: {color: '#aaccff', tooltipStyle: 'wiki'}}, - ], - }); - - quickSnapshot('tags in name escaped', [ - {args: ['localized.track', { - directory: 'foo', - name: `<a href="SNOOPING">AS USUAL</a> I SEE`, - }]}, - {args: ['localized.track', { - directory: 'bar', - name: `<b>boldface</b>`, - }]}, - {args: ['localized.album', { - directory: 'exile', - name: '>Exile<', - }]}, - {args: ['localized.track', { - directory: 'heart', - name: '<3', - }]}, - ]); - - quickSnapshot('nested links in content stripped', { - args: ['localized.staticPage', {directory: 'foo', name: 'Foo'}], - slots: { - content: - html.tag('b', {[html.joinChildren]: ''}, [ - html.tag('a', {href: 'bar'}, `Oooo!`), - ` Very spooky.`, - ]), - }, - }); -}); diff --git a/test/snapshot/transformContent.js b/test/snapshot/transformContent.js deleted file mode 100644 index 63391681..00000000 --- a/test/snapshot/transformContent.js +++ /dev/null @@ -1,171 +0,0 @@ -import t from 'tap'; -import {testContentFunctions} from '#test-lib'; - -testContentFunctions(t, 'transformContent (snapshot)', async (t, evaluate) => { - await evaluate.load({ - mock: { - image: evaluate.stubContentFunction('image'), - }, - }); - - const extraDependencies = { - wikiData: { - albumData: [ - {directory: 'cool-album', name: 'Cool Album', color: '#123456'}, - ], - }, - - to: (key, ...args) => `to-${key}/${args.join('/')}`, - }; - - const quickSnapshot = (message, content, slots) => - evaluate.snapshot(message, { - name: 'transformContent', - args: [content], - extraDependencies, - slots, - }); - - quickSnapshot( - 'two text paragraphs', - `Hello, world!\n` + - `Wow, this is very cool.`); - - quickSnapshot( - 'links to a thing', - `This is [[album:cool-album|my favorite album]].\n` + - `That's right, [[album:cool-album]]!`); - - quickSnapshot( - 'indent on a directly following line', - `<div>\n` + - ` <span>Wow!</span>\n` + - `</div>`); - - quickSnapshot( - 'indent on an indierctly following line', - `Some text.\n` + - `Yes, some more text.\n` + - `\n` + - ` I am hax0rz!!\n` + - ` All yor base r blong 2 us.\n` + - `\n` + - `Aye.\n` + - `Aye aye aye.`); - - quickSnapshot( - 'hanging indent list', - `Hello!\n` + - `\n` + - `* I am a list item and I\n` + - ` go on and on and on\n` + - ` and on and on and on.\n` + - `\n` + - `* I am another list item.\n` + - ` Yeah.\n` + - `\n` + - `In-between!\n` + - `\n` + - `* Spooky,\n` + - ` spooky, I say!\n` + - `* Following list item.\n` + - ` No empty line around me.\n` + - `* Very cool.\n` + - ` So, so cool.\n` + - `\n` + - `Goodbye!`); - - quickSnapshot( - 'inline images', - `<img src="snooping.png"> as USUAL...\n` + - `What do you know? <img src="cowabunga.png" width="24" height="32">\n` + - `[[album:cool-album|I'm on the left.]]<img src="im-on-the-right.jpg">\n` + - `<img src="im-on-the-left.jpg">[[album:cool-album|I'm on the right.]]\n` + - `Media time! <img src="media/misc/interesting.png"> Oh yeah!\n` + - `<img src="must.png"><img src="stick.png"><img src="together.png">\n` + - `And... all done! <img src="end-of-source.png">`); - - quickSnapshot( - 'non-inline image #1', - `<img src="spark.png">`); - - quickSnapshot( - 'non-inline image #2', - `Rad.\n` + - `<img src="spark.png">`); - - quickSnapshot( - 'non-inline image #3', - `<img src="spark.png">\n` + - `Baller.`); - - quickSnapshot( - 'dates', - `[[date:2023-04-13]] Yep!\n` + - `Very nice: [[date:25 October 2413]]`); - - quickSnapshot( - 'super basic string', - `Neat listing: [[string:listingPage.listAlbums.byDate.title]]`); - - quickSnapshot( - 'basic markdown', - `Hello *world!* This is **SO COOL.**`); - - quickSnapshot( - 'escape entire tag', - `\\[[album:cool-album|spooky]] [[album:cool-album|scary]]`); - - quickSnapshot( - 'escape end of tag', - `My favorite album is [[album:cool-album|[Tactical Omission\\]]].\n` + - `Your favorite album is [[album:cool-album|[Tactical Wha-Huh-Now]]].`); - - quickSnapshot( - 'escape markdown', - `What will it be, *ye fool?* \\*arr*`); - - quickSnapshot( - 'lyrics - basic line breaks', - `Hey, ho\n` + - `And away we go\n` + - `Truly, music\n` + - `\n` + - `(Oh yeah)\n` + - `(That's right)`, - {mode: 'lyrics'}); - - quickSnapshot( - 'lyrics - repeated and edge line breaks', - `\n\nWell, you know\nHow it goes\n\n\nYessiree\n\n\n`, - {mode: 'lyrics'}); - - quickSnapshot( - 'lyrics - line breaks around tags', - `The date be [[date:13 April 2004]]\n` + - `I say, the date be [[date:13 April 2004]]\n` + - `[[date:13 April 2004]]\n` + - `[[date:13 April 2004]][[date:13 April 2004]][[date:13 April 2004]]\n` + - `(Aye!)\n` + - `\n` + - `[[date:13 April 2004]]\n` + - `[[date:13 April 2004]][[date:13 April 2004]]\n` + - `[[date:13 April 2004]]\n` + - `\n` + - `[[date:13 April 2004]]\n` + - `[[date:13 April 2004]], and don't ye forget it`, - {mode: 'lyrics'}); - - quickSnapshot( - 'emails', - `Email cute dogs to qznebula@protonmail.com please.\n` + - `Just kidding... [unless?](mailto:qznebula@protonmail.com)`); - - quickSnapshot( - `absorb punctuation`, - `Don't you think this is an [interesting pull request](https://github.com/hsmusic/hsmusic-wiki/pull/567), Steve?\n` + - `Aren't you [interested](https://github.com/hsmusic/hsmusic-wiki/pull/567)... in [checking it out](https://github.com/hsmusic/hsmusic-wiki/pull/567)?!!`); - - // TODO: Snapshots for mode: inline - // TODO: Snapshots for mode: single-link -}); diff --git a/test/lib/strict-match-error.js b/test/test-lib.js index e3b36e93..a12974cd 100644 --- a/test/lib/strict-match-error.js +++ b/test/test-lib.js @@ -1,3 +1,39 @@ +import {compositeFrom} from '#composite'; + +Error.stackTraceLimit = Infinity; + +export function quickCheckCompositeOutputs(t, dependencies) { + return (step, outputDict) => { + t.same( + Object.keys(step.toDescription().outputs), + Object.keys(outputDict)); + + const composite = compositeFrom({ + compose: false, + steps: [ + step, + + { + dependencies: Object.keys(outputDict), + + // Access all dependencies by their expected keys - + // the composition runner actually provides a proxy + // and is checking that *we* access the dependencies + // we've specified. + compute: dependencies => + Object.fromEntries( + Object.keys(outputDict) + .map(key => [key, dependencies[key]])), + }, + ], + }); + + t.same( + composite.expose.compute(dependencies), + outputDict); + }; +} + export function strictlyThrows(t, fn, pattern) { const error = catchErrorOrNull(fn); diff --git a/test/unit/content/dependencies/generateAlbumTrackList.js b/test/unit/content/dependencies/generateAlbumTrackList.js deleted file mode 100644 index 988f8505..00000000 --- a/test/unit/content/dependencies/generateAlbumTrackList.js +++ /dev/null @@ -1,43 +0,0 @@ -import t from 'tap'; -import {testContentFunctions} from '#test-lib'; - -testContentFunctions(t, 'generateAlbumTrackList (unit)', async (t, evaluate) => { - await evaluate.load({ - mock: { - generateAlbumTrackListItem: { - extraDependencies: ['html'], - data: track => track.name, - generate: (name, {html}) => - html.tag('li', `Item: ${name}`), - }, - - image: - evaluate.stubContentFunction('image'), - }, - }); - - let readDuration = false; - - const track = (name, duration) => ({ - name, - get duration() { - readDuration = true; - return duration; - }, - }); - - const tracks = [ - track('Track 1', 30), - track('Track 2', 15), - ]; - - evaluate({ - name: 'generateAlbumTrackList', - args: [{ - trackSections: [{isDefaultTrackSection: true, tracks}], - tracks, - }], - }); - - t.notOk(readDuration, 'expect no access to track.duration property'); -}); diff --git a/test/unit/content/dependencies/linkArtist.js b/test/unit/content/dependencies/linkArtist.js deleted file mode 100644 index e6e19d2f..00000000 --- a/test/unit/content/dependencies/linkArtist.js +++ /dev/null @@ -1,31 +0,0 @@ -import t from 'tap'; -import {testContentFunctions} from '#test-lib'; - -testContentFunctions(t, 'linkArtist (unit)', async (t, evaluate) => { - const artistObject = {}; - const linkTemplate = {}; - - await evaluate.load({ - mock: evaluate.mock(mock => ({ - linkThing: { - relations: mock.function('linkThing.relations', () => ({})) - .args([undefined, 'localized.artist', artistObject]) - .once(), - - data: mock.function('linkThing.data', () => ({})) - .args(['localized.artist', artistObject]) - .once(), - - generate: mock.function('linkThing.data', () => linkTemplate) - .once(), - } - })), - }); - - const result = evaluate({ - name: 'linkArtist', - args: [artistObject], - }); - - t.equal(result, linkTemplate); -}); diff --git a/test/unit/content/dependencies/linkContribution.js b/test/unit/content/dependencies/linkContribution.js deleted file mode 100644 index 3ffd71d2..00000000 --- a/test/unit/content/dependencies/linkContribution.js +++ /dev/null @@ -1,137 +0,0 @@ -import t from 'tap'; -import {testContentFunctions} from '#test-lib'; - -t.test('generateContributionLinks (unit)', async t => { - const artist1 = { - name: 'Clark Powell', - directory: 'clark-powell', - urls: ['https://soundcloud.com/plazmataz'], - }; - - const artist2 = { - name: 'Grounder & Scratch', - directory: 'the-big-baddies', - urls: [], - }; - - const artist3 = { - name: 'Toby Fox', - directory: 'toby-fox', - urls: ['https://tobyfox.bandcamp.com/', 'https://toby.fox/'], - }; - - const annotation1 = null; - const annotation2 = 'Snooping'; - const annotation3 = 'Arrangement'; - - await testContentFunctions(t, 'generateContributionLinks (unit 1)', async (t, evaluate) => { - const slots = { - showAnnotation: true, - showExternalLinks: true, - }; - - await evaluate.load({ - mock: evaluate.mock(mock => ({ - linkArtist: { - relations: mock - .function('linkArtist.relations', () => ({})) - .args([undefined, artist1]).next() - .args([undefined, artist2]).next() - .args([undefined, artist3]), - - data: mock - .function('linkArtist.data', () => ({})) - .args([artist1]).next() - .args([artist2]).next() - .args([artist3]), - - // This can be tweaked to return a specific (mocked) template - // for each artist if we need to test for slots in the future. - generate: mock.function('linkArtist.generate', () => 'artist link') - .repeat(3), - }, - - generateExternalIcon: { - data: mock - .function('generateExternalIcon.data', () => ({})) - .args([artist1.urls[0]]).next() - .args([artist3.urls[0]]).next() - .args([artist3.urls[1]]), - - generate: mock - .function('generateExternalIcon.generate', () => ({ - toString: () => 'icon', - setSlot: () => {}, - })) - .repeat(3), - } - })), - }); - - evaluate({ - name: 'linkContribution', - multiple: [ - {args: [{artist: artist1, annotation: annotation1}]}, - {args: [{artist: artist2, annotation: annotation2}]}, - {args: [{artist: artist3, annotation: annotation3}]}, - ], - slots, - }); - }); - - await testContentFunctions(t, 'generateContributionLinks (unit 2)', async (t, evaluate) => { - const slots = { - showAnnotation: false, - showExternalLinks: false, - }; - - await evaluate.load({ - mock: evaluate.mock(mock => ({ - linkArtist: { - relations: mock - .function('linkArtist.relations', () => ({})) - .args([undefined, artist1]).next() - .args([undefined, artist2]).next() - .args([undefined, artist3]), - - data: mock - .function('linkArtist.data', () => ({})) - .args([artist1]).next() - .args([artist2]).next() - .args([artist3]), - - generate: mock - .function(() => 'artist link') - .repeat(3), - }, - - // Even though icons are hidden, these are still called! The dependency - // tree is the same since whether or not the external icon links are - // shown is dependent on a slot, which is undefined and arbitrary at - // relations/data time (it might change on a whim at generate time). - generateExternalIcon: { - data: mock - .function('generateExternalIcon.data', () => ({})) - .repeat(3), - - generate: mock - .function('generateExternalIcon.generate', () => ({ - toString: () => 'icon', - setSlot: () => {}, - })) - .repeat(3), - }, - })), - }); - - evaluate({ - name: 'linkContribution', - multiple: [ - {args: [{artist: artist1, annotation: annotation1}]}, - {args: [{artist: artist2, annotation: annotation2}]}, - {args: [{artist: artist3, annotation: annotation3}]}, - ], - slots, - }); - }); -}); diff --git a/test/unit/data/cacheable-object.js b/test/unit/data/cacheable-object.js index 4b927248..d0448025 100644 --- a/test/unit/data/cacheable-object.js +++ b/test/unit/data/cacheable-object.js @@ -2,10 +2,14 @@ import t from 'tap'; import CacheableObject from '#cacheable-object'; -function newCacheableObject(PD) { - return new (class extends CacheableObject { - static [CacheableObject.propertyDescriptors] = PD; - }); +function newCacheableObject(propertyDescriptors) { + const constructor = class extends CacheableObject { + static [CacheableObject.propertyDescriptors] = propertyDescriptors; + }; + + constructor.finalizeCacheableObjectPrototype(); + + return Reflect.construct(constructor, []); } t.test(`CacheableObject simple separate update & expose`, t => { @@ -22,8 +26,8 @@ t.test(`CacheableObject simple separate update & expose`, t => { }, expose: { - dependencies: ['number'], - compute: ({ number }) => number * 2 + dependencies: ['_number'], + compute: ({ _number: number }) => number * 2 } } }); @@ -49,8 +53,8 @@ t.test(`CacheableObject basic cache behavior`, t => { }, expose: { - dependencies: ['string'], - compute: ({ string }) => { + dependencies: ['_string'], + compute: ({ _string: string }) => { computeCount++; return string.toUpperCase(); } @@ -132,8 +136,8 @@ t.test(`CacheableObject combined update & expose (transform with dependency)`, t }, expose: { - dependencies: ['times'], - transform: (value, { times }) => value.repeat(times) + dependencies: ['_times'], + transform: (value, { _times: times }) => value.repeat(times) } }, diff --git a/test/unit/data/composite/control-flow/exposeConstant.js b/test/unit/data/composite/control-flow/exposeConstant.js deleted file mode 100644 index 0c75894b..00000000 --- a/test/unit/data/composite/control-flow/exposeConstant.js +++ /dev/null @@ -1,42 +0,0 @@ -import t from 'tap'; - -import {compositeFrom, continuationSymbol, input} from '#composite'; -import {exposeConstant} from '#composite/control-flow'; - -t.test(`exposeConstant: basic behavior`, t => { - t.plan(2); - - const composite1 = compositeFrom({ - compose: false, - - steps: [ - exposeConstant({ - value: input.value('foo'), - }), - ], - }); - - t.match(composite1, { - expose: { - dependencies: [], - }, - }); - - t.equal(composite1.expose.compute(), 'foo'); -}); - -t.test(`exposeConstant: validate inputs`, t => { - t.plan(2); - - t.throws( - () => exposeConstant({}), - {message: `Errors in input options passed to exposeConstant`, errors: [ - {message: `Required these inputs: value`}, - ]}); - - t.throws( - () => exposeConstant({value: 'some dependency'}), - {message: `Errors in input options passed to exposeConstant`, errors: [ - {message: `value: Expected input.value() call, got dependency name`}, - ]}); -}); diff --git a/test/unit/data/composite/control-flow/exposeDependency.js b/test/unit/data/composite/control-flow/exposeDependency.js deleted file mode 100644 index 8f6bfd01..00000000 --- a/test/unit/data/composite/control-flow/exposeDependency.js +++ /dev/null @@ -1,64 +0,0 @@ -import t from 'tap'; - -import {compositeFrom, continuationSymbol, input} from '#composite'; -import {exposeDependency} from '#composite/control-flow'; - -t.test(`exposeDependency: basic behavior`, t => { - t.plan(4); - - const composite1 = compositeFrom({ - compose: false, - - steps: [ - exposeDependency({dependency: 'foo'}), - ], - }); - - t.match(composite1, { - expose: { - dependencies: ['foo'], - }, - }); - - t.equal(composite1.expose.compute({foo: 'bar'}), 'bar'); - - const composite2 = compositeFrom({ - compose: false, - - steps: [ - { - dependencies: ['foo'], - compute: (continuation, {foo}) => - continuation({'#bar': foo.toUpperCase()}), - }, - - exposeDependency({dependency: '#bar'}), - ], - }); - - t.match(composite2, { - expose: { - dependencies: ['foo'], - }, - }); - - t.equal(composite2.expose.compute({foo: 'bar'}), 'BAR'); -}); - -t.test(`exposeDependency: validate inputs`, t => { - t.plan(2); - - t.throws( - () => exposeDependency({}), - {message: `Errors in input options passed to exposeDependency`, errors: [ - {message: `Required these inputs: dependency`}, - ]}); - - t.throws( - () => exposeDependency({ - dependency: input.value('some static value'), - }), - {message: `Errors in input options passed to exposeDependency`, errors: [ - {message: `dependency: Expected dependency name, got input.value() call`}, - ]}); -}); diff --git a/test/unit/data/composite/control-flow/withResultOfAvailabilityCheck.js b/test/unit/data/composite/control-flow/withResultOfAvailabilityCheck.js deleted file mode 100644 index 9d588e4c..00000000 --- a/test/unit/data/composite/control-flow/withResultOfAvailabilityCheck.js +++ /dev/null @@ -1,197 +0,0 @@ -import t from 'tap'; - -import {compositeFrom, continuationSymbol, input} from '#composite'; -import {withResultOfAvailabilityCheck} from '#composite/control-flow'; - -const composite = compositeFrom({ - compose: false, - - steps: [ - withResultOfAvailabilityCheck({ - from: 'from', - mode: 'mode', - }).outputs({ - ['#availability']: '#result', - }), - - { - dependencies: ['#result'], - compute: ({'#result': result}) => result, - }, - ], -}); - -t.test(`withResultOfAvailabilityCheck: basic behavior`, t => { - t.plan(1); - - t.match(composite, { - expose: { - dependencies: ['from', 'mode'], - }, - }); -}); - -const quickCompare = (t, expect, {from, mode}) => - t.equal(composite.expose.compute({from, mode}), expect); - -const quickThrows = (t, {from, mode}) => - t.throws(() => composite.expose.compute({from, mode})); - -t.test(`withResultOfAvailabilityCheck: mode = null`, t => { - t.plan(11); - - quickCompare(t, true, {mode: 'null', from: 'truthy string'}); - quickCompare(t, true, {mode: 'null', from: 123}); - quickCompare(t, true, {mode: 'null', from: true}); - - quickCompare(t, true, {mode: 'null', from: ''}); - quickCompare(t, true, {mode: 'null', from: 0}); - quickCompare(t, true, {mode: 'null', from: -1}); - quickCompare(t, true, {mode: 'null', from: false}); - - quickCompare(t, true, {mode: 'null', from: [1, 2, 3]}); - quickCompare(t, true, {mode: 'null', from: []}); - - quickCompare(t, false, {mode: 'null', from: null}); - quickCompare(t, false, {mode: 'null', from: undefined}); -}); - -t.test(`withResultOfAvailabilityCheck: mode = empty`, t => { - t.plan(11); - - quickThrows(t, {mode: 'empty', from: 'truthy string'}); - quickThrows(t, {mode: 'empty', from: 123}); - quickThrows(t, {mode: 'empty', from: true}); - - quickThrows(t, {mode: 'empty', from: ''}); - quickThrows(t, {mode: 'empty', from: 0}); - quickThrows(t, {mode: 'empty', from: -1}); - quickThrows(t, {mode: 'empty', from: false}); - - quickCompare(t, true, {mode: 'empty', from: [1, 2, 3]}); - quickCompare(t, false, {mode: 'empty', from: []}); - - quickCompare(t, false, {mode: 'empty', from: null}); - quickCompare(t, false, {mode: 'empty', from: undefined}); -}); - -t.test(`withResultOfAvailabilityCheck: mode = falsy`, t => { - t.plan(11); - - quickCompare(t, true, {mode: 'falsy', from: 'truthy string'}); - quickCompare(t, true, {mode: 'falsy', from: 123}); - quickCompare(t, true, {mode: 'falsy', from: true}); - - quickCompare(t, false, {mode: 'falsy', from: ''}); - quickCompare(t, false, {mode: 'falsy', from: 0}); - quickCompare(t, true, {mode: 'falsy', from: -1}); - quickCompare(t, false, {mode: 'falsy', from: false}); - - quickCompare(t, true, {mode: 'falsy', from: [1, 2, 3]}); - quickCompare(t, false, {mode: 'falsy', from: []}); - - quickCompare(t, false, {mode: 'falsy', from: null}); - quickCompare(t, false, {mode: 'falsy', from: undefined}); -}); - -t.test(`withResultOfAvailabilityCheck: mode = index`, t => { - t.plan(11); - - quickCompare(t, false, {mode: 'index', from: 'truthy string'}); - quickCompare(t, true, {mode: 'index', from: 123}); - quickCompare(t, false, {mode: 'index', from: true}); - - quickCompare(t, false, {mode: 'index', from: ''}); - quickCompare(t, true, {mode: 'index', from: 0}); - quickCompare(t, false, {mode: 'index', from: -1}); - quickCompare(t, false, {mode: 'index', from: false}); - - quickCompare(t, false, {mode: 'index', from: [1, 2, 3]}); - quickCompare(t, false, {mode: 'index', from: []}); - - quickCompare(t, false, {mode: 'index', from: null}); - quickCompare(t, false, {mode: 'index', from: undefined}); -}); - -t.test(`withResultOfAvailabilityCheck: default mode`, t => { - t.plan(1); - - const template = withResultOfAvailabilityCheck({ - from: 'foo', - }); - - t.match(template.toDescription(), { - inputMapping: { - from: input.dependency('foo'), - mode: input.value('null'), - }, - }); -}); - -t.test(`withResultOfAvailabilityCheck: validate static inputs`, t => { - t.plan(5); - - t.throws( - () => withResultOfAvailabilityCheck({}), - {message: `Errors in input options passed to withResultOfAvailabilityCheck`, errors: [ - {message: `Required these inputs: from`}, - ]}); - - t.doesNotThrow(() => - withResultOfAvailabilityCheck({ - from: 'dependency1', - mode: 'dependency2', - })); - - t.doesNotThrow(() => - withResultOfAvailabilityCheck({ - from: input.value('some static value'), - mode: input.value('null'), - })); - - t.throws( - () => withResultOfAvailabilityCheck({ - from: 'foo', - mode: input.value('invalid'), - }), - {message: `Errors in input options passed to withResultOfAvailabilityCheck`, errors: [ - {message: `mode: Expected one of null empty falsy index, got invalid`}, - ]}); - - t.throws(() => - withResultOfAvailabilityCheck({ - from: input.value(null), - mode: input.value(null), - }), - {message: `Errors in input options passed to withResultOfAvailabilityCheck`, errors: [ - {message: `mode: Expected a value, got null`}, - ]}); -}); - -t.test(`withResultOfAvailabilityCheck: validate dynamic inputs`, t => { - t.plan(2); - - t.throws( - () => composite.expose.compute({ - from: 'apple', - mode: 'banana', - }), - {message: `Error computing composition`, cause: - {message: `Error in step 1 of 2, withResultOfAvailabilityCheck`, cause: - {message: `Error computing composition withResultOfAvailabilityCheck`, cause: - {message: `Errors in input values provided to withResultOfAvailabilityCheck`, errors: [ - {message: `mode: Expected one of null empty falsy index, got banana`}, - ]}}}}); - - t.throws( - () => composite.expose.compute({ - from: null, - mode: null, - }), - {message: `Error computing composition`, cause: - {message: `Error in step 1 of 2, withResultOfAvailabilityCheck`, cause: - {message: `Error computing composition withResultOfAvailabilityCheck`, cause: - {message: `Errors in input values provided to withResultOfAvailabilityCheck`, errors: [ - {message: `mode: Expected a value, got null`}, - ]}}}}); -}); diff --git a/test/unit/data/composite/data/withPropertiesFromObject.js b/test/unit/data/composite/data/withPropertiesFromObject.js deleted file mode 100644 index b81d51a5..00000000 --- a/test/unit/data/composite/data/withPropertiesFromObject.js +++ /dev/null @@ -1,241 +0,0 @@ -import t from 'tap'; -import {quickCheckCompositeOutputs} from '#test-lib'; - -import {compositeFrom, input} from '#composite'; -import {exposeDependency} from '#composite/control-flow'; -import {withPropertiesFromObject} from '#composite/data'; - -const composite = compositeFrom({ - compose: false, - - steps: [ - withPropertiesFromObject({ - object: 'object', - properties: 'properties', - }), - - exposeDependency({dependency: '#object'}), - ], -}); - -t.test(`withPropertiesFromObject: basic behavior`, t => { - t.plan(4); - - t.match(composite, { - expose: { - dependencies: ['object', 'properties'], - }, - }); - - t.same( - composite.expose.compute({ - object: {foo: 'bar', bim: 'BOOM', bam: 'baz'}, - properties: ['foo', 'bim'], - }), - {foo: 'bar', bim: 'BOOM'}); - - t.same( - composite.expose.compute({ - object: {value1: 'uwah', value2: 'arah'}, - properties: ['value1', 'value3'], - }), - {value1: 'uwah', value3: null}); - - t.same( - composite.expose.compute({ - object: null, - properties: ['ohMe', 'ohMy', 'ohDear'], - }), - {ohMe: null, ohMy: null, ohDear: null}); -}); - -t.test(`withPropertiesFromObject: output shapes & values`, t => { - t.plan(2 * 2 * 3 ** 2); - - const dependencies = { - ['object_dependency']: - {foo: 'apple', bar: 'banana', baz: 'orange'}, - [input('object_neither')]: - {foo: 'koala', bar: 'okapi', baz: 'mongoose'}, - ['properties_dependency']: - ['foo', 'bar', 'missing1'], - [input('properties_neither')]: - ['foo', 'baz', 'missing3'], - }; - - const qcco = quickCheckCompositeOutputs(t, dependencies); - - const mapLevel1 = [ - [input.value('prefix_value'), [ - ['object_dependency', [ - ['properties_dependency', { - '#object': {foo: 'apple', bar: 'banana', missing1: null}, - }], - [input.value(['bar', 'baz', 'missing2']), { - '#prefix_value.bar': 'banana', - '#prefix_value.baz': 'orange', - '#prefix_value.missing2': null, - }], - [input('properties_neither'), { - '#object': {foo: 'apple', baz: 'orange', missing3: null}, - }]]], - - [input.value({foo: 'ouh', bar: 'rah', baz: 'nyu'}), [ - ['properties_dependency', { - '#object': {foo: 'ouh', bar: 'rah', missing1: null}, - }], - [input.value(['bar', 'baz', 'missing2']), { - '#prefix_value.bar': 'rah', - '#prefix_value.baz': 'nyu', - '#prefix_value.missing2': null, - }], - [input('properties_neither'), { - '#object': {foo: 'ouh', baz: 'nyu', missing3: null}, - }]]], - - [input('object_neither'), [ - ['properties_dependency', { - '#object': {foo: 'koala', bar: 'okapi', missing1: null}, - }], - [input.value(['bar', 'baz', 'missing2']), { - '#prefix_value.bar': 'okapi', - '#prefix_value.baz': 'mongoose', - '#prefix_value.missing2': null, - }], - [input('properties_neither'), { - '#object': {foo: 'koala', baz: 'mongoose', missing3: null}, - }]]]]], - - [input.value(null), [ - ['object_dependency', [ - ['properties_dependency', { - '#object': {foo: 'apple', bar: 'banana', missing1: null}, - }], - [input.value(['bar', 'baz', 'missing2']), { - '#object_dependency.bar': 'banana', - '#object_dependency.baz': 'orange', - '#object_dependency.missing2': null, - }], - [input('properties_neither'), { - '#object': {foo: 'apple', baz: 'orange', missing3: null}, - }]]], - - [input.value({foo: 'ouh', bar: 'rah', baz: 'nyu'}), [ - ['properties_dependency', { - '#object': {foo: 'ouh', bar: 'rah', missing1: null}, - }], - [input.value(['bar', 'baz', 'missing2']), { - '#object.bar': 'rah', - '#object.baz': 'nyu', - '#object.missing2': null, - }], - [input('properties_neither'), { - '#object': {foo: 'ouh', baz: 'nyu', missing3: null}, - }]]], - - [input('object_neither'), [ - ['properties_dependency', { - '#object': {foo: 'koala', bar: 'okapi', missing1: null}, - }], - [input.value(['bar', 'baz', 'missing2']), { - '#object.bar': 'okapi', - '#object.baz': 'mongoose', - '#object.missing2': null, - }], - [input('properties_neither'), { - '#object': {foo: 'koala', baz: 'mongoose', missing3: null}, - }]]]]], - ]; - - for (const [prefixInput, mapLevel2] of mapLevel1) { - for (const [objectInput, mapLevel3] of mapLevel2) { - for (const [propertiesInput, outputDict] of mapLevel3) { - const step = withPropertiesFromObject({ - prefix: prefixInput, - object: objectInput, - properties: propertiesInput, - }); - - qcco(step, outputDict); - } - } - } -}); - -t.test(`withPropertiesFromObject: validate static inputs`, t => { - t.plan(3); - - t.throws( - () => withPropertiesFromObject({}), - {message: `Errors in input options passed to withPropertiesFromObject`, errors: [ - {message: `Required these inputs: object, properties`}, - ]}); - - t.throws( - () => withPropertiesFromObject({ - object: input.value('intriguing'), - properties: input.value('very'), - prefix: input.value({yes: 'yup'}), - }), - {message: `Errors in input options passed to withPropertiesFromObject`, errors: [ - {message: `object: Expected an object, got string`}, - {message: `properties: Expected an array, got string`}, - {message: `prefix: Expected a string, got object`}, - ]}); - - t.throws( - () => withPropertiesFromObject({ - object: input.value([['abc', 1], ['def', 2], [123, 3]]), - properties: input.value(['abc', 'def', 123]), - }), - {message: `Errors in input options passed to withPropertiesFromObject`, errors: [ - {message: `object: Expected an object, got array`}, - {message: `properties: Errors validating array items`, errors: [ - { - [Symbol.for('hsmusic.annotateError.indexInSourceArray')]: 2, - message: `Error at zero-index 2: 123`, - cause: { - message: `Expected a string, got number`, - }, - }, - ]}, - ]}); -}); - -t.test(`withPropertiesFromObject: validate dynamic inputs`, t => { - t.plan(2); - - t.throws( - () => composite.expose.compute({ - object: 'intriguing', - properties: 'onceMore', - }), - {message: `Error computing composition`, cause: - {message: `Error in step 1 of 2, withPropertiesFromObject`, cause: - {message: `Error computing composition withPropertiesFromObject`, cause: - {message: `Errors in input values provided to withPropertiesFromObject`, errors: [ - {message: `object: Expected an object, got string`}, - {message: `properties: Expected an array, got string`}, - ]}}}}); - - t.throws( - () => composite.expose.compute({ - object: [['abc', 1], ['def', 2], [123, 3]], - properties: ['abc', 'def', 123], - }), - {message: `Error computing composition`, cause: - {message: `Error in step 1 of 2, withPropertiesFromObject`, cause: - {message: `Error computing composition withPropertiesFromObject`, cause: - {message: `Errors in input values provided to withPropertiesFromObject`, errors: [ - {message: `object: Expected an object, got array`}, - {message: `properties: Errors validating array items`, errors: [ - { - [Symbol.for('hsmusic.annotateError.indexInSourceArray')]: 2, - message: `Error at zero-index 2: 123`, - cause: { - message: `Expected a string, got number`, - }, - }, - ]}, - ]}}}}); -}); diff --git a/test/unit/data/composite/data/withPropertyFromObject.js b/test/unit/data/composite/data/withPropertyFromObject.js deleted file mode 100644 index 912c924c..00000000 --- a/test/unit/data/composite/data/withPropertyFromObject.js +++ /dev/null @@ -1,191 +0,0 @@ -import t from 'tap'; -import {quickCheckCompositeOutputs} from '#test-lib'; - -import CacheableObject from '#cacheable-object'; -import {compositeFrom, input} from '#composite'; -import {exposeDependency} from '#composite/control-flow'; -import {withPropertyFromObject} from '#composite/data'; - -t.test(`withPropertyFromObject: basic behavior`, t => { - t.plan(4); - - const composite = compositeFrom({ - compose: false, - - steps: [ - withPropertyFromObject({ - object: 'object', - property: 'property', - }), - - exposeDependency({dependency: '#value'}), - ], - }); - - t.match(composite, { - expose: { - dependencies: ['object', 'property'], - }, - }); - - t.equal(composite.expose.compute({ - object: {foo: 'bar', bim: 'BOOM'}, - property: 'bim', - }), 'BOOM'); - - t.equal(composite.expose.compute({ - object: {value1: 'uwah'}, - property: 'value2', - }), null); - - t.equal(composite.expose.compute({ - object: null, - property: 'oml where did me object go', - }), null); -}); - -t.test(`withPropertyFromObject: "internal" input`, t => { - t.plan(7); - - const composite = compositeFrom({ - compose: false, - - steps: [ - withPropertyFromObject({ - object: 'object', - property: 'property', - internal: 'internal', - }), - - exposeDependency({dependency: '#value'}), - ], - }); - - const thing = new (class extends CacheableObject { - static [CacheableObject.propertyDescriptors] = { - foo: { - flags: {update: true, expose: false}, - }, - - bar: { - flags: {update: true, expose: true}, - }, - - baz: { - flags: {update: true, expose: true}, - expose: { - transform: baz => baz * 2, - }, - }, - }; - }); - - thing.foo = 100; - thing.bar = 200; - thing.baz = 300; - - t.match(composite, { - expose: { - dependencies: ['object', 'property', 'internal'], - }, - }); - - t.equal(composite.expose.compute({ - object: thing, - property: 'foo', - internal: true, - }), 100); - - t.equal(composite.expose.compute({ - object: thing, - property: 'bar', - internal: true, - }), 200); - - t.equal(composite.expose.compute({ - object: thing, - property: 'baz', - internal: true, - }), 300); - - t.equal(composite.expose.compute({ - object: thing, - property: 'baz', - internal: false, - }), 600); - - t.equal(composite.expose.compute({ - object: thing, - property: 'bimbam', - internal: false, - }), null); - - t.equal(composite.expose.compute({ - object: null, - property: 'bambim', - internal: false, - }), null); -}); - -t.test(`withPropertyFromObject: output shapes & values`, t => { - t.plan(2 * 3 ** 2); - - const dependencies = { - ['object_dependency']: - {foo: 'apple', bar: 'banana', baz: 'orange'}, - [input('object_neither')]: - {foo: 'koala', bar: 'okapi', baz: 'mongoose'}, - ['property_dependency']: - 'foo', - [input('property_neither')]: - 'baz', - }; - - const qcco = quickCheckCompositeOutputs(t, dependencies); - - const mapLevel1 = [ - ['object_dependency', [ - ['property_dependency', { - '#value': 'apple', - }], - [input.value('bar'), { - '#object_dependency.bar': 'banana', - }], - [input('property_neither'), { - '#value': 'orange', - }]]], - - [input.value({foo: 'ouh', bar: 'rah', baz: 'nyu'}), [ - ['property_dependency', { - '#value': 'ouh', - }], - [input.value('bar'), { - '#value': 'rah', - }], - [input('property_neither'), { - '#value': 'nyu', - }]]], - - [input('object_neither'), [ - ['property_dependency', { - '#value': 'koala', - }], - [input.value('bar'), { - '#value': 'okapi', - }], - [input('property_neither'), { - '#value': 'mongoose', - }]]], - ]; - - for (const [objectInput, mapLevel2] of mapLevel1) { - for (const [propertyInput, outputDict] of mapLevel2) { - const step = withPropertyFromObject({ - object: objectInput, - property: propertyInput, - }); - - qcco(step, outputDict); - } - } -}); diff --git a/test/unit/data/composite/data/withUniqueItemsOnly.js b/test/unit/data/composite/data/withUniqueItemsOnly.js deleted file mode 100644 index 50b16f43..00000000 --- a/test/unit/data/composite/data/withUniqueItemsOnly.js +++ /dev/null @@ -1,69 +0,0 @@ -import t from 'tap'; -import {quickCheckCompositeOutputs} from '#test-lib'; - -import {compositeFrom, input} from '#composite'; -import {exposeDependency} from '#composite/control-flow'; -import {withUniqueItemsOnly} from '#composite/data'; - -t.test(`withUniqueItemsOnly: basic behavior`, t => { - t.plan(3); - - const composite = compositeFrom({ - compose: false, - - steps: [ - withUniqueItemsOnly({ - list: 'list', - }), - - exposeDependency({dependency: '#list'}), - ], - }); - - t.match(composite, { - expose: { - dependencies: ['list'], - }, - }); - - t.same(composite.expose.compute({ - list: ['apple', 'banana', 'banana', 'banana', 'apple', 'watermelon'], - }), ['apple', 'banana', 'watermelon']); - - t.same(composite.expose.compute({ - list: [], - }), []); -}); - -t.test(`withUniqueItemsOnly: output shapes & values`, t => { - t.plan(2 * 3 ** 1); - - const dependencies = { - ['list_dependency']: - [1, 1, 2, 3, 3, 4, 'foo', false, false, 4], - [input('list_neither')]: - [8, 8, 7, 6, 6, 5, 'bar', true, true, 5], - }; - - const qcco = quickCheckCompositeOutputs(t, dependencies); - - const mapLevel1 = [ - ['list_dependency', { - '#list_dependency': [1, 2, 3, 4, 'foo', false], - }], - [input.value([-1, -1, 'interesting', 'very', 'interesting']), { - '#uniqueItems': [-1, 'interesting', 'very'], - }], - [input('list_neither'), { - '#uniqueItems': [8, 7, 6, 5, 'bar', true], - }], - ]; - - for (const [listInput, outputDict] of mapLevel1) { - const step = withUniqueItemsOnly({ - list: listInput, - }); - - qcco(step, outputDict); - } -}); diff --git a/test/unit/data/composite/things/track/withAlbum.js b/test/unit/data/composite/things/track/withAlbum.js deleted file mode 100644 index 6f50776b..00000000 --- a/test/unit/data/composite/things/track/withAlbum.js +++ /dev/null @@ -1,119 +0,0 @@ -import t from 'tap'; - -import '#import-heck'; - -import Thing from '#thing'; - -import {compositeFrom, input} from '#composite'; -import {exposeConstant, exposeDependency} from '#composite/control-flow'; -import {withAlbum} from '#composite/things/track'; - -t.test(`withAlbum: basic behavior`, t => { - t.plan(3); - - const composite = compositeFrom({ - compose: false, - steps: [ - withAlbum(), - exposeDependency({dependency: '#album'}), - ], - }); - - t.match(composite, { - expose: { - dependencies: ['albumData', 'this'], - }, - }); - - const fakeTrack1 = { - [Thing.isThing]: true, - directory: 'foo', - }; - - const fakeTrack2 = { - [Thing.isThing]: true, - directory: 'bar', - }; - - const fakeAlbum = { - [Thing.isThing]: true, - directory: 'baz', - tracks: [fakeTrack1], - }; - - t.equal( - composite.expose.compute({ - albumData: [fakeAlbum], - this: fakeTrack1, - }), - fakeAlbum); - - t.equal( - composite.expose.compute({ - albumData: [fakeAlbum], - this: fakeTrack2, - }), - null); -}); - -t.test(`withAlbum: early exit conditions`, t => { - t.plan(4); - - const composite = compositeFrom({ - compose: false, - steps: [ - withAlbum(), - exposeConstant({ - value: input.value('bimbam'), - }), - ], - }); - - const fakeTrack1 = { - [Thing.isThing]: true, - directory: 'foo', - }; - - const fakeTrack2 = { - [Thing.isThing]: true, - directory: 'bar', - }; - - const fakeAlbum = { - [Thing.isThing]: true, - directory: 'baz', - tracks: [fakeTrack1], - }; - - t.equal( - composite.expose.compute({ - albumData: [fakeAlbum], - this: fakeTrack1, - }), - 'bimbam', - `does not early exit if albumData is present and contains the track`); - - t.equal( - composite.expose.compute({ - albumData: [fakeAlbum], - this: fakeTrack2, - }), - 'bimbam', - `does not early exit if albumData is present and does not contain the track`); - - t.equal( - composite.expose.compute({ - albumData: [], - this: fakeTrack1, - }), - 'bimbam', - `does not early exit if albumData is empty array`); - - t.equal( - composite.expose.compute({ - albumData: null, - this: fakeTrack1, - }), - null, - `early exits if albumData is null`); -}); diff --git a/test/unit/data/composite/wiki-data/withParsedCommentaryEntries.js b/test/unit/data/composite/wiki-data/withParsedCommentaryEntries.js deleted file mode 100644 index 411fd11d..00000000 --- a/test/unit/data/composite/wiki-data/withParsedCommentaryEntries.js +++ /dev/null @@ -1,373 +0,0 @@ -import t from 'tap'; - -import {compositeFrom, input} from '#composite'; -import thingConstructors from '#things'; - -import {exposeDependency} from '#composite/control-flow'; -import {withParsedCommentaryEntries} from '#composite/wiki-data'; - -const {Artist} = thingConstructors; - -const composite = compositeFrom({ - compose: false, - - steps: [ - withParsedCommentaryEntries({ - from: 'from', - }), - - exposeDependency({dependency: '#parsedCommentaryEntries'}), - ], -}); - -function stubArtist(artistName = `Test Artist`) { - const artist = new Artist(); - artist.name = artistName; - - return artist; -} - -t.test(`withParsedCommentaryEntries: basic behavior`, t => { - t.plan(7); - - const artist1 = stubArtist(`Mobius Trip`); - const artist2 = stubArtist(`Hadron Kaleido`); - const artist3 = stubArtist('Homestuck'); - - const artistData = [artist1, artist2, artist3]; - - t.match(composite, { - expose: { - dependencies: ['from', 'artistData'], - }, - }); - - t.same(composite.expose.compute({ - artistData, - from: - `<i>Mobius Trip:</i>\n` + - `Some commentary.\n` + - `Very cool.\n`, - }), [ - { - artists: [artist1], - artistDisplayText: null, - annotation: null, - date: null, - accessDate: null, - accessKind: null, - secondDate: null, - dateKind: null, - body: `Some commentary.\nVery cool.`, - }, - ]); - - t.same(composite.expose.compute({ - artistData, - from: - `<i>Mobius Trip|Moo-bius Trip:</i> (music, art, 12 January 2015)\n` + - `First commentary entry.\n` + - `Very cool.\n` + - `<i>Hadron Kaleido|<b>[[artist:hadron-kaleido|The Ol' Hadron]]</b>:</i> (moral support, 4/4/2022)\n` + - `Second commentary entry. Yes. So cool.\n` + - `<i>Mystery Artist:</i> (pingas, August 25, 2023)\n` + - `Oh no.. Oh dear...\n` + - `<i>Mobius Trip, Hadron Kaleido:</i>\n` + - `And back around we go.`, - }), [ - { - artists: [artist1], - artistDisplayText: `Moo-bius Trip`, - annotation: `music, art`, - date: new Date('12 January 2015'), - body: `First commentary entry.\nVery cool.`, - secondDate: null, - dateKind: null, - accessDate: null, - accessKind: null, - }, - { - artists: [artist2], - artistDisplayText: `<b>[[artist:hadron-kaleido|The Ol' Hadron]]</b>`, - annotation: `moral support`, - date: new Date('4 April 2022'), - body: `Second commentary entry. Yes. So cool.`, - secondDate: null, - dateKind: null, - accessDate: null, - accessKind: null, - }, - { - artists: [], - artistDisplayText: null, - annotation: `pingas`, - date: new Date('25 August 2023'), - body: `Oh no.. Oh dear...`, - secondDate: null, - dateKind: null, - accessDate: null, - accessKind: null, - }, - { - artists: [artist1, artist2], - artistDisplayText: null, - annotation: null, - date: null, - body: `And back around we go.`, - secondDate: null, - dateKind: null, - accessDate: null, - accessKind: null, - }, - ]); - - t.same(composite.expose.compute({ - artistData, - from: - `<i>Homestuck:</i> ([Bandcamp credits blurb](https://web.archive.org/web/20201024170202/https://homestuck.bandcamp.com/track/sburban-countdown-3) on "Homestuck Vol. 1-4 (with Midnight Crew: Drawing Dead)", 10/25/2019)\n` + - `\n` + - `Written by [[artist:michael-guy-bowman|Michael Guy Bowman]]<br>\n` + - `Arrangement by [[artist:mark-j-hadley|Mark Hadley]]\n` + - `\n` + - `<i>Homestuck:</i> ([fake](https://web.archive.org/web/20201024170202/https://homestuck.bandcamp.com/fake), 7/20/2019 captured 4/13/2024)\n` + - `This isn't real!\n` + - `\n` + - `<i>Homestuck:</i> ([fake](https://homestuck.com/fake), 10/25/2011 accessed 10/27/2011)\n` + - `This isn't real either!\n` + - `\n` + - `<i>Homestuck:</i> ([fake](https://web.archive.org/web/20201024170202/https://homestuck.bandcamp.com/fake), 7/20/2019 accessed 4/13/2024)\n` + - `Not this one, neither!\n` - }), [ - { - artists: [artist3], - artistDisplayText: null, - annotation: `[Bandcamp credits blurb](https://web.archive.org/web/20201024170202/https://homestuck.bandcamp.com/track/sburban-countdown-3) on "Homestuck Vol. 1-4 (with Midnight Crew: Drawing Dead)"`, - date: new Date('10/25/2019'), - body: - `Written by [[artist:michael-guy-bowman|Michael Guy Bowman]]<br>\n` + - `Arrangement by [[artist:mark-j-hadley|Mark Hadley]]`, - secondDate: null, - dateKind: null, - accessDate: new Date('10/24/2020'), - accessKind: 'captured', - }, - { - artists: [artist3], - artistDisplayText: null, - annotation: `[fake](https://web.archive.org/web/20201024170202/https://homestuck.bandcamp.com/fake)`, - date: new Date('7/20/2019'), - body: `This isn't real!`, - secondDate: null, - dateKind: null, - accessDate: new Date('4/13/2024'), - accessKind: 'captured', - }, - { - artists: [artist3], - artistDisplayText: null, - annotation: `[fake](https://homestuck.com/fake)`, - date: new Date('10/25/2011'), - body: `This isn't real either!`, - secondDate: null, - dateKind: null, - accessDate: new Date('10/27/2011'), - accessKind: 'accessed', - }, - { - artists: [artist3], - artistDisplayText: null, - annotation: `[fake](https://web.archive.org/web/20201024170202/https://homestuck.bandcamp.com/fake)`, - date: new Date('7/20/2019'), - body: `Not this one, neither!`, - secondDate: null, - dateKind: null, - accessDate: new Date('4/13/2024'), - accessKind: 'accessed', - }, - ]); - - t.same(composite.expose.compute({ - artistData, - from: - `<i>Homestuck:</i> ([MSPA sound credits](https://web.archive.org/web/20120805031705/http://www.mspaintadventures.com:80/soundcredits.html), sometime 6/21/2012 - 8/5/2012)\n` + - `\n` + - `[[flash:246|Page 2146]] - <b>"Sburban Countdown"</b><br>\n` + - `Available on Bandcamp in [[album:homestuck-vol-1-4|Homestuck Vol. 1-4]]<br>\n` + - `Written by [[artist:michael-guy-bowman|Michael Guy Bowman]]<br>\n` + - `Arrangement by [[artist:mark-j-hadley|Mark Hadley]]\n` + - `\n` + - `<i>Homestuck:</i> ([fake](https://web.archive.org/web/20201024170202/https://homestuck.bandcamp.com/fake), 7/20/2019 - 7/20/2022 captured 4/13/2024)\n` + - `It's goin' once.\n` + - `\n` + - `<i>Homestuck:</i> (10/25/2011 - 10/28/2011 accessed 10/27/2011)\n` + - `It's goin' twice.\n` + - `\n` + - `<i>Homestuck:</i> ([fake](https://web.archive.org/web/20201024170202/https://homestuck.bandcamp.com/fake), 7/20/2019 - 7/20/2022 accessed 4/13/2024)\n` + - `It's goin' thrice!\n` - }), [ - { - artists: [artist3], - artistDisplayText: null, - annotation: `[MSPA sound credits](https://web.archive.org/web/20120805031705/http://www.mspaintadventures.com:80/soundcredits.html)`, - body: - `[[flash:246|Page 2146]] - <b>"Sburban Countdown"</b><br>\n` + - `Available on Bandcamp in [[album:homestuck-vol-1-4|Homestuck Vol. 1-4]]<br>\n` + - `Written by [[artist:michael-guy-bowman|Michael Guy Bowman]]<br>\n` + - `Arrangement by [[artist:mark-j-hadley|Mark Hadley]]`, - date: new Date('6/21/2012'), - secondDate: new Date('8/5/2012'), - dateKind: 'sometime', - accessDate: new Date('8/5/2012'), - accessKind: 'captured', - }, - { - artists: [artist3], - artistDisplayText: null, - annotation: `[fake](https://web.archive.org/web/20201024170202/https://homestuck.bandcamp.com/fake)`, - body: `It's goin' once.`, - date: new Date('7/20/2019'), - secondDate: new Date('7/20/2022'), - dateKind: null, - accessDate: new Date('4/13/2024'), - accessKind: 'captured', - }, - { - artists: [artist3], - artistDisplayText: null, - annotation: '', // TODO: This should be null, but the regex isn't structured for that, at the moment. - body: `It's goin' twice.`, - date: new Date('10/25/2011'), - secondDate: new Date('10/28/2011'), - dateKind: null, - accessDate: new Date('10/27/2011'), - accessKind: 'accessed', - - }, - { - artists: [artist3], - artistDisplayText: null, - annotation: `[fake](https://web.archive.org/web/20201024170202/https://homestuck.bandcamp.com/fake)`, - body: `It's goin' thrice!`, - date: new Date('7/20/2019'), - secondDate: new Date('7/20/2022'), - dateKind: null, - accessDate: new Date('4/13/2024'), - accessKind: 'accessed', - }, - ]); - - t.same(composite.expose.compute({ - artistData, - from: - `<i>Homestuck:</i> ([MSPA sound credits](https://web.archive.org/web/20120805031705/http://www.mspaintadventures.com:80/soundcredits.html), sometime 6/21/2012 - 8/5/2012)\n` + - `\n` + - `[[flash:246|Page 2146]] - <b>"Sburban Countdown"</b><br>\n` + - `Available on Bandcamp in [[album:homestuck-vol-1-4|Homestuck Vol. 1-4]]<br>\n` + - `Written by [[artist:michael-guy-bowman|Michael Guy Bowman]]<br>\n` + - `Arrangement by [[artist:mark-j-hadley|Mark Hadley]]\n` + - `\n` + - `<i>Homestuck:</i> ([fake](https://web.archive.org/web/20201024170202/https://homestuck.bandcamp.com/fake), 7/20/2019 - 7/20/2022 captured 4/13/2024)\n` + - `It's goin' once.\n` + - `\n` + - `<i>Homestuck:</i> (10/25/2011 - 10/28/2011 accessed 10/27/2011)\n` + - `It's goin' twice.\n` + - `\n` + - `<i>Homestuck:</i> ([fake](https://web.archive.org/web/20201024170202/https://homestuck.bandcamp.com/fake), 7/20/2019 - 7/20/2022 accessed 4/13/2024)\n` + - `It's goin' thrice!\n` - }), [ - { - artists: [artist3], - artistDisplayText: null, - annotation: `[MSPA sound credits](https://web.archive.org/web/20120805031705/http://www.mspaintadventures.com:80/soundcredits.html)`, - body: - `[[flash:246|Page 2146]] - <b>"Sburban Countdown"</b><br>\n` + - `Available on Bandcamp in [[album:homestuck-vol-1-4|Homestuck Vol. 1-4]]<br>\n` + - `Written by [[artist:michael-guy-bowman|Michael Guy Bowman]]<br>\n` + - `Arrangement by [[artist:mark-j-hadley|Mark Hadley]]`, - date: new Date('6/21/2012'), - secondDate: new Date('8/5/2012'), - dateKind: 'sometime', - accessDate: new Date('8/5/2012'), - accessKind: 'captured', - }, - { - artists: [artist3], - artistDisplayText: null, - annotation: `[fake](https://web.archive.org/web/20201024170202/https://homestuck.bandcamp.com/fake)`, - body: `It's goin' once.`, - date: new Date('7/20/2019'), - secondDate: new Date('7/20/2022'), - dateKind: null, - accessDate: new Date('4/13/2024'), - accessKind: 'captured', - }, - { - artists: [artist3], - artistDisplayText: null, - annotation: '', // TODO: This should be null, but the regex isn't structured for that, at the moment. - body: `It's goin' twice.`, - date: new Date('10/25/2011'), - secondDate: new Date('10/28/2011'), - dateKind: null, - accessDate: new Date('10/27/2011'), - accessKind: 'accessed', - - }, - { - artists: [artist3], - artistDisplayText: null, - annotation: `[fake](https://web.archive.org/web/20201024170202/https://homestuck.bandcamp.com/fake)`, - body: `It's goin' thrice!`, - date: new Date('7/20/2019'), - secondDate: new Date('7/20/2022'), - dateKind: null, - accessDate: new Date('4/13/2024'), - accessKind: 'accessed', - }, - ]); - - t.same(composite.expose.compute({ - artistData, - from: - `<i>Homestuck:</i> ([Homestuck sound credits](https://web.archive.org/web/20180717171235/https://www.homestuck.com/credits/sound), excerpt, around 4/3/2018)\n` + - `blablabla\n` + - `<i>Homestuck:</i> ([fake](https://web.archive.org/web/20201024170202/https://homestuck.bandcamp.com/fake), around 7/20/2019 - 7/20/2022 captured 4/13/2024)\n` + - `Snoopin', snoopin', snoo,\n` + - `<i>Homestuck:</i> ([fake](https://web.archive.org/web/20201024170202/https://homestuck.bandcamp.com/fake), throughout 7/20/2019 - 7/20/2022 accessed 4/13/2024)\n` + - `~ pingas ~\n` - }), [ - { - artists: [artist3], - artistDisplayText: null, - annotation: `[Homestuck sound credits](https://web.archive.org/web/20180717171235/https://www.homestuck.com/credits/sound), excerpt`, - body: `blablabla`, - date: new Date('4/3/2018'), - secondDate: null, - dateKind: 'around', - accessDate: new Date('7/17/2018'), - accessKind: 'captured', - }, - { - artists: [artist3], - artistDisplayText: null, - annotation: `[fake](https://web.archive.org/web/20201024170202/https://homestuck.bandcamp.com/fake)`, - body: `Snoopin', snoopin', snoo,`, - date: new Date('7/20/2019'), - secondDate: new Date('7/20/2022'), - dateKind: 'around', - accessDate: new Date('4/13/2024'), - accessKind: 'captured', - }, - { - artists: [artist3], - artistDisplayText: null, - annotation: `[fake](https://web.archive.org/web/20201024170202/https://homestuck.bandcamp.com/fake)`, - body: `~ pingas ~`, - date: new Date('7/20/2019'), - secondDate: new Date('7/20/2022'), - dateKind: 'throughout', - accessDate: new Date('4/13/2024'), - accessKind: 'accessed', - }, - ]); -}); diff --git a/test/unit/data/things/album.js b/test/unit/data/things/album.js deleted file mode 100644 index a64488f7..00000000 --- a/test/unit/data/things/album.js +++ /dev/null @@ -1,469 +0,0 @@ -import t from 'tap'; - -import thingConstructors from '#things'; - -import { - linkAndBindWikiData, - stubArtistAndContribs, - stubThing, - stubWikiData, -} from '#test-lib'; - -t.test(`Album.artTags`, t => { - const {Album, ArtTag} = thingConstructors; - - t.plan(3); - - const wikiData = stubWikiData(); - - const {contribs} = stubArtistAndContribs(wikiData); - const album = stubThing(wikiData, Album); - const tag1 = stubThing(wikiData, ArtTag, {name: `Tag 1`}); - const tag2 = stubThing(wikiData, ArtTag, {name: `Tag 2`}); - - linkAndBindWikiData(wikiData); - - t.same(album.artTags, [], - `artTags #1: defaults to empty array`); - - album.artTags = [`Tag 1`, `Tag 2`]; - - t.same(album.artTags, [], - `artTags #2: is empty if album doesn't have cover artists`); - - album.coverArtistContribs = contribs; - - t.same(album.artTags, [tag1, tag2], - `artTags #3: resolves if album has cover artists`); -}); - -t.test(`Album.bannerDimensions`, t => { - const {Album} = thingConstructors; - - t.plan(4); - - const wikiData = stubWikiData(); - - const album = stubThing(wikiData, Album); - const {contribs, badContribs} = stubArtistAndContribs(wikiData); - - linkAndBindWikiData(wikiData); - - t.equal(album.bannerDimensions, null, - `Album.bannerDimensions #1: defaults to null`); - - album.bannerDimensions = [1200, 275]; - - t.equal(album.bannerDimensions, null, - `Album.bannerDimensions #2: is null if bannerArtistContribs empty`); - - album.bannerArtistContribs = badContribs; - - t.equal(album.bannerDimensions, null, - `Album.bannerDimensions #3: is null if bannerArtistContribs resolves empty`); - - album.bannerArtistContribs = contribs; - - t.same(album.bannerDimensions, [1200, 275], - `Album.bannerDimensions #4: is own value`); -}); - -t.test(`Album.bannerFileExtension`, t => { - const {Album} = thingConstructors; - - t.plan(5); - - const wikiData = stubWikiData(); - - const album = stubThing(wikiData, Album); - const {contribs, badContribs} = stubArtistAndContribs(wikiData); - - linkAndBindWikiData(wikiData); - - t.equal(album.bannerFileExtension, null, - `Album.bannerFileExtension #1: defaults to null`); - - album.bannerFileExtension = 'png'; - - t.equal(album.bannerFileExtension, null, - `Album.bannerFileExtension #2: is null if bannerArtistContribs empty`); - - album.bannerArtistContribs = badContribs; - - t.equal(album.bannerFileExtension, null, - `Album.bannerFileExtension #3: is null if bannerArtistContribs resolves empty`); - - album.bannerArtistContribs = contribs; - - t.equal(album.bannerFileExtension, 'png', - `Album.bannerFileExtension #4: is own value`); - - album.bannerFileExtension = null; - - t.equal(album.bannerFileExtension, 'jpg', - `Album.bannerFileExtension #5: defaults to jpg`); -}); - -t.test(`Album.bannerStyle`, t => { - const {Album} = thingConstructors; - - t.plan(4); - - const wikiData = stubWikiData(); - - const album = stubThing(wikiData, Album); - const {contribs, badContribs} = stubArtistAndContribs(wikiData); - - linkAndBindWikiData(wikiData); - - t.equal(album.bannerStyle, null, - `Album.bannerStyle #1: defaults to null`); - - album.bannerStyle = `opacity: 0.5`; - - t.equal(album.bannerStyle, null, - `Album.bannerStyle #2: is null if bannerArtistContribs empty`); - - album.bannerArtistContribs = badContribs; - - t.equal(album.bannerStyle, null, - `Album.bannerStyle #3: is null if bannerArtistContribs resolves empty`); - - album.bannerArtistContribs = contribs; - - t.equal(album.bannerStyle, `opacity: 0.5`, - `Album.bannerStyle #4: is own value`); -}); - -t.test(`Album.coverArtDate`, t => { - const {Album} = thingConstructors; - - t.plan(6); - - const wikiData = stubWikiData(); - - const album = stubThing(wikiData, Album); - const {contribs, badContribs} = stubArtistAndContribs(wikiData); - - linkAndBindWikiData(wikiData); - - t.equal(album.coverArtDate, null, - `Album.coverArtDate #1: defaults to null`); - - album.date = new Date('2012-10-25'); - - t.equal(album.coverArtDate, null, - `Album.coverArtDate #2: is null if coverArtistContribs empty (1/2)`); - - album.coverArtDate = new Date('2011-04-13'); - - t.equal(album.coverArtDate, null, - `Album.coverArtDate #3: is null if coverArtistContribs empty (2/2)`); - - album.coverArtistContribs = contribs; - - t.same(album.coverArtDate, new Date('2011-04-13'), - `Album.coverArtDate #4: is own value`); - - album.coverArtDate = null; - - t.same(album.coverArtDate, new Date(`2012-10-25`), - `Album.coverArtDate #5: inherits album release date`); - - album.coverArtistContribs = badContribs; - - t.equal(album.coverArtDate, null, - `Album.coverArtDate #6: is null if coverArtistContribs resolves empty`); -}); - -t.test(`Album.coverArtFileExtension`, t => { - const {Album} = thingConstructors; - - t.plan(5); - - const wikiData = stubWikiData(); - - const album = stubThing(wikiData, Album); - const {contribs, badContribs} = stubArtistAndContribs(wikiData); - - linkAndBindWikiData(wikiData); - - t.equal(album.coverArtFileExtension, null, - `Album.coverArtFileExtension #1: is null if coverArtistContribs empty (1/2)`); - - album.coverArtFileExtension = 'png'; - - t.equal(album.coverArtFileExtension, null, - `Album.coverArtFileExtension #2: is null if coverArtistContribs empty (2/2)`); - - album.coverArtFileExtension = null; - album.coverArtistContribs = contribs; - - t.equal(album.coverArtFileExtension, 'jpg', - `Album.coverArtFileExtension #3: defaults to jpg`); - - album.coverArtFileExtension = 'png'; - - t.equal(album.coverArtFileExtension, 'png', - `Album.coverArtFileExtension #4: is own value`); - - album.coverArtistContribs = badContribs; - - t.equal(album.coverArtFileExtension, null, - `Album.coverArtFileExtension #5: is null if coverArtistContribs resolves empty`); -}); - -t.test(`Album.tracks`, t => { - const {Album, Track, TrackSection} = thingConstructors; - - t.plan(4); - - let wikiData = stubWikiData(); - - const album = stubThing(wikiData, Album); - album.directory = 'foo'; - - const track1 = stubThing(wikiData, Track, {directory: 'track1'}); - const track2 = stubThing(wikiData, Track, {directory: 'track2'}); - const track3 = stubThing(wikiData, Track, {directory: 'track3'}); - const tracks = [track1, track2, track3]; - - const section1 = stubThing(wikiData, TrackSection, {unqualifiedDirectory: 'section1'}); - const section2 = stubThing(wikiData, TrackSection, {unqualifiedDirectory: 'section2'}); - const section3 = stubThing(wikiData, TrackSection, {unqualifiedDirectory: 'section3'}); - const section4 = stubThing(wikiData, TrackSection, {unqualifiedDirectory: 'section4'}); - const section5 = stubThing(wikiData, TrackSection, {unqualifiedDirectory: 'section5'}); - const section6 = stubThing(wikiData, TrackSection, {unqualifiedDirectory: 'section6'}); - const sections = [section1, section2, section3, section4, section5, section6]; - - wikiData = null; - - for (const track of tracks) { - track.albumData = [album]; - } - - for (const section of sections) { - section.albumData = [album]; - } - - t.same(album.tracks, [], - `Album.tracks #1: defaults to empty array`); - - section1.tracks = [track1, track2, track3]; - - album.trackSections = [section1]; - - t.same(album.tracks, [track1, track2, track3], - `Album.tracks #2: pulls tracks from one track section`); - - section1.tracks = [track1]; - section2.tracks = [track2, track3]; - - album.trackSections = [section1, section2]; - - t.same(album.tracks, [track1, track2, track3], - `Album.tracks #3: pulls tracks from multiple track sections`); - - section1.tracks = [track1]; - section2.tracks = []; - section3.tracks = [track2]; - section4.tracks = []; - section5.tracks = []; - section6.tracks = [track3]; - - album.trackSections = [section1, section2, section3, section4, section5, section6]; - - t.same(album.tracks, [track1, track2, track3], - `Album.tracks #4: skips empty track sections`); -}); - -t.test(`Album.trackSections`, t => { - const {Album, Track, TrackSection} = thingConstructors; - - t.plan(7); - - let wikiData = stubWikiData(); - - const album = stubThing(wikiData, Album); - - const track1 = stubThing(wikiData, Track, {directory: 'track1'}); - const track2 = stubThing(wikiData, Track, {directory: 'track2'}); - const track3 = stubThing(wikiData, Track, {directory: 'track3'}); - const track4 = stubThing(wikiData, Track, {directory: 'track4'}); - const tracks = [track1, track2, track3, track4]; - - const section1 = stubThing(wikiData, TrackSection, {unqualifiedDirectory: 'section1'}); - const section2 = stubThing(wikiData, TrackSection, {unqualifiedDirectory: 'section2'}); - const section3 = stubThing(wikiData, TrackSection, {unqualifiedDirectory: 'section3'}); - const section4 = stubThing(wikiData, TrackSection, {unqualifiedDirectory: 'section4'}); - const section5 = stubThing(wikiData, TrackSection, {unqualifiedDirectory: 'section5'}); - const sections = [section1, section2, section3, section4, section5]; - - wikiData = null; - - for (const track of tracks) { - track.albumData = [album]; - } - - for (const section of sections) { - section.albumData = [album]; - } - - section1.tracks = [track1, track2]; - section2.tracks = [track3, track4]; - - album.trackSections = [section1, section2]; - - t.match(album.trackSections, [ - {tracks: [track1, track2]}, - {tracks: [track3, track4]}, - ], `Album.trackSections #1: exposes tracks`); - - t.match(album.trackSections, [ - {tracks: [track1, track2], startIndex: 0}, - {tracks: [track3, track4], startIndex: 2}, - ], `Album.trackSections #2: exposes startIndex`); - - section1.tracks = [track1]; - section2.tracks = [track2]; - section3.tracks = [track3]; - - section1.name = 'First section'; - section2.name = 'Second section'; - - album.trackSections = [section1, section2, section3]; - - t.match(album.trackSections, [ - {name: 'First section', tracks: [track1]}, - {name: 'Second section', tracks: [track2]}, - {name: 'Unnamed Track Section', tracks: [track3]}, - ], `Album.trackSections #3: exposes name, with fallback value`); - - album.color = '#123456'; - - section2.color = '#abcdef'; - - // XXX_decacheWikiData - album.trackSections = []; - album.trackSections = [section1, section2, section3]; - - t.match(album.trackSections, [ - {tracks: [track1], color: '#123456'}, - {tracks: [track2], color: '#abcdef'}, - {tracks: [track3], color: '#123456'}, - ], `Album.trackSections #4: exposes color, inherited from album`); - - section2.dateOriginallyReleased = new Date('2009-04-11'); - - // XXX_decacheWikiData - album.trackSections = []; - album.trackSections = [section1, section2, section3]; - - t.match(album.trackSections, [ - {tracks: [track1], dateOriginallyReleased: null}, - {tracks: [track2], dateOriginallyReleased: new Date('2009-04-11')}, - {tracks: [track3], dateOriginallyReleased: null}, - ], `Album.trackSections #5: exposes dateOriginallyReleased, if present`); - - section1.isDefaultTrackSection = true; - section2.isDefaultTrackSection = false; - - // XXX_decacheWikiData - album.trackSections = []; - album.trackSections = [section1, section2, section3]; - - t.match(album.trackSections, [ - {tracks: [track1], isDefaultTrackSection: true}, - {tracks: [track2], isDefaultTrackSection: false}, - {tracks: [track3], isDefaultTrackSection: false}, - ], `Album.trackSections #6: exposes isDefaultTrackSection, defaults to false`); - - section1.tracks = [track1, track2]; - section2.tracks = [track3]; - section3.tracks = []; - section4.tracks = []; - section5.tracks = [track4]; - - section1.color = '#112233'; - section2.color = '#334455'; - section3.color = '#bbbbba'; - section4.color = '#556677'; - section5.color = '#778899'; - - album.trackSections = [section1, section2, section3, section4, section5]; - - t.match(album.trackSections, [ - {tracks: [track1, track2], color: '#112233'}, - {tracks: [track3], color: '#334455'}, - {tracks: [], color: '#bbbbba'}, - {tracks: [], color: '#556677'}, - {tracks: [track4], color: '#778899'}, - ], `Album.trackSections #7: keeps empty sections`); -}); - -t.test(`Album.wallpaperFileExtension`, t => { - const {Album} = thingConstructors; - - t.plan(5); - - const wikiData = stubWikiData(); - - const album = stubThing(wikiData, Album); - const {contribs, badContribs} = stubArtistAndContribs(wikiData); - - linkAndBindWikiData(wikiData); - - t.equal(album.wallpaperFileExtension, null, - `Album.wallpaperFileExtension #1: defaults to null`); - - album.wallpaperFileExtension = 'png'; - - t.equal(album.wallpaperFileExtension, null, - `Album.wallpaperFileExtension #2: is null if wallpaperArtistContribs empty`); - - album.wallpaperArtistContribs = contribs; - - t.equal(album.wallpaperFileExtension, 'png', - `Album.wallpaperFileExtension #3: is own value`); - - album.wallpaperFileExtension = null; - - t.equal(album.wallpaperFileExtension, 'jpg', - `Album.wallpaperFileExtension #4: defaults to jpg`); - - album.wallpaperArtistContribs = badContribs; - - t.equal(album.wallpaperFileExtension, null, - `Album.wallpaperFileExtension #5: is null if wallpaperArtistContribs resolves empty`); -}); - -t.test(`Album.wallpaperStyle`, t => { - const {Album} = thingConstructors; - - t.plan(4); - - const wikiData = stubWikiData(); - - const album = stubThing(wikiData, Album); - const {contribs, badContribs} = stubArtistAndContribs(wikiData); - - linkAndBindWikiData(wikiData); - - t.equal(album.wallpaperStyle, null, - `Album.wallpaperStyle #1: defaults to null`); - - album.wallpaperStyle = `opacity: 0.5`; - - t.equal(album.wallpaperStyle, null, - `Album.wallpaperStyle #2: is null if wallpaperArtistContribs empty`); - - album.wallpaperArtistContribs = badContribs; - - t.equal(album.wallpaperStyle, null, - `Album.wallpaperStyle #3: is null if wallpaperArtistContribs resolves empty`); - - album.wallpaperArtistContribs = contribs; - - t.equal(album.wallpaperStyle, `opacity: 0.5`, - `Album.wallpaperStyle #4: is own value`); -}); diff --git a/test/unit/data/things/art-tag.js b/test/unit/data/things/art-tag.js deleted file mode 100644 index 015acd0e..00000000 --- a/test/unit/data/things/art-tag.js +++ /dev/null @@ -1,26 +0,0 @@ -import t from 'tap'; - -import thingConstructors from '#things'; - -t.test(`ArtTag.nameShort`, t => { - const {ArtTag} = thingConstructors; - - t.plan(3); - - const artTag = new ArtTag(); - - artTag.name = `Dave Strider`; - - t.equal(artTag.nameShort, `Dave Strider`, - `ArtTag #1: defaults to name`); - - artTag.name = `Dave Strider (Homestuck)`; - - t.equal(artTag.nameShort, `Dave Strider`, - `ArtTag #2: trims parenthical part at end`); - - artTag.name = `This (And) That (Then)`; - - t.equal(artTag.nameShort, `This (And) That`, - `ArtTag #2: doesn't trim midlde parenthical part`); -}); diff --git a/test/unit/data/things/flash.js b/test/unit/data/things/flash.js deleted file mode 100644 index de39e80d..00000000 --- a/test/unit/data/things/flash.js +++ /dev/null @@ -1,40 +0,0 @@ -import t from 'tap'; - -import thingConstructors from '#things'; - -import { - linkAndBindWikiData, - stubThing, - stubWikiData, -} from '#test-lib'; - -t.test(`Flash.color`, t => { - const {Flash, FlashAct} = thingConstructors; - - t.plan(4); - - const wikiData = stubWikiData(); - - const flash = stubThing(wikiData, Flash, {directory: 'my-flash'}); - const flashAct = stubThing(wikiData, FlashAct, {flashes: ['flash:my-flash']}); - - const {XXX_decacheWikiData} = linkAndBindWikiData(wikiData); - - t.equal(flash.color, null, - `color #1: defaults to null`); - - flashAct.color = '#abcdef'; - XXX_decacheWikiData(); - - t.equal(flash.color, '#abcdef', - `color #2: inherits from flash act`); - - flash.color = '#123456'; - - t.equal(flash.color, '#123456', - `color #3: is own value`); - - t.throws(() => { flash.color = '#aeiouw'; }, - {cause: TypeError}, - `color #4: must be set to valid color`); -}); diff --git a/test/unit/data/things/track.js b/test/unit/data/things/track.js deleted file mode 100644 index 403dc064..00000000 --- a/test/unit/data/things/track.js +++ /dev/null @@ -1,752 +0,0 @@ -import t from 'tap'; - -import {bindFind} from '#find'; -import thingConstructors from '#things'; - -import { - linkAndBindWikiData, - stubArtistAndContribs, - stubFlashAndAct, - stubThing, - stubTrackAndAlbum, - stubWikiData, -} from '#test-lib'; - -t.test(`Track.album`, t => { - const {Album, Track, TrackSection} = thingConstructors; - - t.plan(6); - - // Note: These asserts use manual albumData/trackData relationships - // to illustrate more specifically the properties which are expected to - // be relevant for this case. Other properties use the same underlying - // get-album behavior as Track.album so aren't tested as aggressively. - - let wikiData = stubWikiData(); - - const track1 = stubThing(wikiData, Track, {directory: 'track1'}); - const track2 = stubThing(wikiData, Track, {directory: 'track2'}); - const album1 = stubThing(wikiData, Album); - const album2 = stubThing(wikiData, Album); - const section1 = stubThing(wikiData, TrackSection, {unqualifiedDirectory: 'section1'}); - const section2 = stubThing(wikiData, TrackSection, {unqualifiedDirectory: 'section2'}); - - wikiData = null; - - t.equal(track1.album, null, - `album #1: defaults to null`); - - track1.albumData = [album1, album2]; - track2.albumData = [album1, album2]; - section1.tracks = [track1]; - section2.tracks = [track2]; - section1.albumData = [album1]; - section2.albumData = [album2]; - album1.trackSections = [section1]; - album2.trackSections = [section2]; - - t.equal(track1.album, album1, - `album #2: is album when album's trackSections matches track`); - - track1.albumData = [album2, album1]; - - t.equal(track1.album, album1, - `album #3: is album when albumData is in different order`); - - track1.albumData = []; - - t.equal(track1.album, null, - `album #4: is null when track missing albumData`); - - section1.tracks = []; - - // XXX_decacheWikiData - album1.trackSections = []; - album1.trackSections = [section1]; - track1.albumData = []; - track1.albumData = [album2, album1]; - - t.equal(track1.album, null, - `album #5: is null when album track section missing tracks`); - - section1.tracks = [track2]; - - // XXX_decacheWikiData - album1.trackSections = []; - album1.trackSections = [section1]; - track1.albumData = []; - track1.albumData = [album2, album1]; - - t.equal(track1.album, null, - `album #6: is null when album track section doesn't match track`); -}); - -t.test(`Track.alwaysReferenceByDirectory`, t => { - t.plan(7); - - const wikiData = stubWikiData(); - - const {track: originalTrack} = - stubTrackAndAlbum(wikiData, 'original-track', 'original-album'); - - const {track: rereleaseTrack, album: rereleaseAlbum} = - stubTrackAndAlbum(wikiData, 'rerelease-track', 'rerelease-album'); - - originalTrack.name = 'Cowabunga'; - rereleaseTrack.name = 'Cowabunga'; - - originalTrack.dataSourceAlbum = 'album:original-album'; - rereleaseTrack.dataSourceAlbum = 'album:rerelease-album'; - - rereleaseTrack.originalReleaseTrack = 'track:original-track'; - - const {XXX_decacheWikiData} = linkAndBindWikiData(wikiData); - - t.equal(originalTrack.alwaysReferenceByDirectory, false, - `alwaysReferenceByDirectory #1: defaults to false`); - - t.equal(rereleaseTrack.alwaysReferenceByDirectory, true, - `alwaysReferenceByDirectory #2: is true if rerelease name matches original`); - - rereleaseTrack.name = 'Foo Dog!'; - - t.equal(rereleaseTrack.alwaysReferenceByDirectory, false, - `alwaysReferenceByDirectory #3: is false if rerelease name doesn't match original`); - - rereleaseTrack.name = `COWabunga`; - - t.equal(rereleaseTrack.alwaysReferenceByDirectory, false, - `alwaysReferenceByDirectory #4: is false if rerelease name doesn't match original exactly`); - - rereleaseAlbum.alwaysReferenceTracksByDirectory = true; - XXX_decacheWikiData(); - - t.equal(rereleaseTrack.alwaysReferenceByDirectory, true, - `alwaysReferenceByDirectory #5: is true if album's alwaysReferenceTracksByDirectory is true`); - - rereleaseTrack.alwaysReferenceByDirectory = false; - - t.equal(rereleaseTrack.alwaysReferenceByDirectory, false, - `alwaysReferenceByDirectory #6: doesn't inherit from album if set to false`); - - rereleaseTrack.name = 'Cowabunga'; - - t.equal(rereleaseTrack.alwaysReferenceByDirectory, false, - `alwaysReferenceByDirectory #7: doesn't compare original release name if set to false`); -}); - -t.test(`Track.artTags`, t => { - const {ArtTag} = thingConstructors; - - t.plan(6); - - const wikiData = stubWikiData(); - - const {track, album} = stubTrackAndAlbum(wikiData); - const {contribs} = stubArtistAndContribs(wikiData); - - const tag1 = - stubThing(wikiData, ArtTag, {name: `Tag 1`}); - - const tag2 = - stubThing(wikiData, ArtTag, {name: `Tag 2`}); - - const {XXX_decacheWikiData} = linkAndBindWikiData(wikiData); - - t.same(track.artTags, [], - `artTags #1: defaults to empty array`); - - track.artTags = [`Tag 1`, `Tag 2`]; - - t.same(track.artTags, [], - `artTags #2: is empty if track doesn't have cover artists`); - - track.coverArtistContribs = contribs; - - t.same(track.artTags, [tag1, tag2], - `artTags #3: resolves if track has cover artists`); - - track.coverArtistContribs = null; - album.trackCoverArtistContribs = contribs; - - XXX_decacheWikiData(); - - t.same(track.artTags, [tag1, tag2], - `artTags #4: resolves if track inherits cover artists`); - - track.disableUniqueCoverArt = true; - - t.same(track.artTags, [], - `artTags #5: is empty if track disables unique cover artwork`); - - album.coverArtistContribs = contribs; - album.artTags = [`Tag 2`]; - - XXX_decacheWikiData(); - - t.notSame(track.artTags, [tag2], - `artTags #6: doesn't inherit from album's art tags`); -}); - -t.test(`Track.artistContribs`, t => { - const {Album, Artist, Track, TrackSection} = thingConstructors; - - t.plan(4); - - const wikiData = stubWikiData(); - - const track = - stubThing(wikiData, Track); - - const section = - stubThing(wikiData, TrackSection, {tracks: [track]}); - - const album = - stubThing(wikiData, Album, {trackSections: [section]}); - - const artist1 = - stubThing(wikiData, Artist, {name: `Artist 1`}); - - const artist2 = - stubThing(wikiData, Artist, {name: `Artist 2`}); - - const {XXX_decacheWikiData} = linkAndBindWikiData(wikiData); - - t.same(track.artistContribs, [], - `artistContribs #1: defaults to empty array`); - - album.artistContribs = [ - {artist: `Artist 1`, annotation: `composition`}, - {artist: `Artist 2`, annotation: null}, - ]; - - XXX_decacheWikiData(); - - t.match(track.artistContribs, - [{artist: artist1, annotation: `composition`}, {artist: artist2, annotation: null}], - `artistContribs #2: inherits album artistContribs`); - - track.artistContribs = [ - {artist: `Artist 1`, annotation: `arrangement`}, - ]; - - t.match(track.artistContribs, [{artist: artist1, annotation: `arrangement`}], - `artistContribs #3: resolves from own value`); - - track.artistContribs = [ - {artist: `Artist 1`, annotation: `snooping`}, - {artist: `Artist 413`, annotation: `as`}, - {artist: `Artist 2`, annotation: `usual`}, - ]; - - t.match(track.artistContribs, - [{artist: artist1, annotation: `snooping`}, {artist: artist2, annotation: `usual`}], - `artistContribs #4: filters out names without matches`); -}); - -t.test(`Track.color`, t => { - t.plan(4); - - const wikiData = stubWikiData(); - - const {track, album, section} = stubTrackAndAlbum(wikiData); - - const {XXX_decacheWikiData} = linkAndBindWikiData(wikiData); - - t.equal(track.color, null, - `color #1: defaults to null`); - - album.color = '#abcdef'; - section.color = '#beeeef'; - - album.trackSections = [section]; - - XXX_decacheWikiData(); - - t.equal(track.color, '#beeeef', - `color #2: inherits from track section before album`); - - track.color = '#123456'; - - t.equal(track.color, '#123456', - `color #3: is own value`); - - t.throws(() => { track.color = '#aeiouw'; }, - {cause: TypeError}, - `color #4: must be set to valid color`); -}); - -t.test(`Track.commentatorArtists`, t => { - const {Artist, Track} = thingConstructors; - - t.plan(8); - - const wikiData = stubWikiData(); - - const track = stubThing(wikiData, Track); - const artist1 = stubThing(wikiData, Artist, {name: `SnooPING`}); - const artist2 = stubThing(wikiData, Artist, {name: `ASUsual`}); - const artist3 = stubThing(wikiData, Artist, {name: `Icy`}); - - linkAndBindWikiData(wikiData); - - // Keep track of the last commentary string in a separate value, since - // the track.commentary property exposes as a completely different format - // (i.e. an array of objects, one for each entry), and so isn't compatible - // with the += operator on its own. - let commentary; - - track.commentary = commentary = - `<i>SnooPING:</i>\n` + - `Wow.\n`; - - t.same(track.commentatorArtists, [artist1], - `Track.commentatorArtists #1: works with one commentator`); - - track.commentary = commentary += - `<i>ASUsual:</i>\n` + - `Yes!\n`; - - t.same(track.commentatorArtists, [artist1, artist2], - `Track.commentatorArtists #2: works with two commentators`); - - track.commentary = commentary += - `<i>Icy|<b>Icy annotation You Did There</b>:</i>\n` + - `Incredible.\n`; - - t.same(track.commentatorArtists, [artist1, artist2, artist3], - `Track.commentatorArtists #3: works with custom artist text`); - - track.commentary = commentary = - `<i>Icy:</i> (project manager)\n` + - `Very good track.\n`; - - t.same(track.commentatorArtists, [artist3], - `Track.commentatorArtists #4: works with annotation`); - - track.commentary = commentary = - `<i>Icy:</i> (project manager, 08/15/2023)\n` + - `Very very good track.\n`; - - t.same(track.commentatorArtists, [artist3], - `Track.commentatorArtists #5: works with date`); - - track.commentary = commentary += - `<i>Ohohohoho:</i>\n` + - `OHOHOHOHOHOHO...\n`; - - t.same(track.commentatorArtists, [artist3], - `Track.commentatorArtists #6: ignores artist names not found`); - - track.commentary = commentary += - `<i>Icy:</i>\n` + - `I'm back!\n`; - - t.same(track.commentatorArtists, [artist3], - `Track.commentatorArtists #7: ignores duplicate artist`); - - track.commentary = commentary += - `<i>SNooPING, ASUsual, Icy:</i>\n` + - `WITH ALL THREE POWERS COMBINED...`; - - t.same(track.commentatorArtists, [artist3, artist1, artist2], - `Track.commentatorArtists #8: works with more than one artist in one entry`); -}); - -t.test(`Track.coverArtistContribs`, t => { - const {Artist} = thingConstructors; - - t.plan(5); - - const wikiData = stubWikiData(); - - const {track, album} = stubTrackAndAlbum(wikiData); - const artist1 = stubThing(wikiData, Artist, {name: `Artist 1`}); - const artist2 = stubThing(wikiData, Artist, {name: `Artist 2`}); - - const {XXX_decacheWikiData} = linkAndBindWikiData(wikiData); - - t.same(track.coverArtistContribs, [], - `coverArtistContribs #1: defaults to empty array`); - - album.trackCoverArtistContribs = [ - {artist: `Artist 1`, annotation: `lines`}, - {artist: `Artist 2`, annotation: null}, - ]; - - XXX_decacheWikiData(); - - t.match(track.coverArtistContribs, - [{artist: artist1, annotation: `lines`}, {artist: artist2, annotation: null}], - `coverArtistContribs #2: inherits album trackCoverArtistContribs`); - - track.coverArtistContribs = [ - {artist: `Artist 1`, annotation: `collage`}, - ]; - - t.match(track.coverArtistContribs, [{artist: artist1, annotation: `collage`}], - `coverArtistContribs #3: resolves from own value`); - - track.coverArtistContribs = [ - {artist: `Artist 1`, annotation: `snooping`}, - {artist: `Artist 413`, annotation: `as`}, - {artist: `Artist 2`, annotation: `usual`}, - ]; - - t.match(track.coverArtistContribs, - [{artist: artist1, annotation: `snooping`}, {artist: artist2, annotation: `usual`}], - `coverArtistContribs #4: filters out names without matches`); - - track.disableUniqueCoverArt = true; - - t.same(track.coverArtistContribs, [], - `coverArtistContribs #5: is empty if track disables unique cover artwork`); -}); - -t.test(`Track.coverArtDate`, t => { - t.plan(8); - - const wikiData = stubWikiData(); - - const {track, album} = stubTrackAndAlbum(wikiData); - const {contribs, badContribs} = stubArtistAndContribs(wikiData); - - const {XXX_decacheWikiData} = linkAndBindWikiData(wikiData); - - track.coverArtistContribs = contribs; - - t.equal(track.coverArtDate, null, - `coverArtDate #1: defaults to null`); - - album.trackArtDate = new Date('2012-12-12'); - - XXX_decacheWikiData(); - - t.same(track.coverArtDate, new Date('2012-12-12'), - `coverArtDate #2: inherits album trackArtDate`); - - track.coverArtDate = new Date('2009-09-09'); - - t.same(track.coverArtDate, new Date('2009-09-09'), - `coverArtDate #3: is own value`); - - track.coverArtistContribs = []; - - t.equal(track.coverArtDate, null, - `coverArtDate #4: is null if track coverArtistContribs empty`); - - album.trackCoverArtistContribs = contribs; - - XXX_decacheWikiData(); - - t.same(track.coverArtDate, new Date('2009-09-09'), - `coverArtDate #5: is not null if album trackCoverArtistContribs specified`); - - album.trackCoverArtistContribs = badContribs; - - XXX_decacheWikiData(); - - t.equal(track.coverArtDate, null, - `coverArtDate #6: is null if album trackCoverArtistContribs resolves empty`); - - track.coverArtistContribs = badContribs; - - t.equal(track.coverArtDate, null, - `coverArtDate #7: is null if track coverArtistContribs resolves empty`); - - track.coverArtistContribs = contribs; - track.disableUniqueCoverArt = true; - - t.equal(track.coverArtDate, null, - `coverArtDate #8: is null if track disables unique cover artwork`); -}); - -t.test(`Track.coverArtFileExtension`, t => { - t.plan(8); - - const wikiData = stubWikiData(); - - const {track, album} = stubTrackAndAlbum(wikiData); - const {contribs} = stubArtistAndContribs(wikiData); - - const {XXX_decacheWikiData} = linkAndBindWikiData(wikiData); - - t.equal(track.coverArtFileExtension, null, - `coverArtFileExtension #1: defaults to null`); - - track.coverArtistContribs = contribs; - - t.equal(track.coverArtFileExtension, 'jpg', - `coverArtFileExtension #2: is jpg if has cover art and not further specified`); - - track.coverArtistContribs = []; - - album.coverArtistContribs = contribs; - XXX_decacheWikiData(); - - t.equal(track.coverArtFileExtension, null, - `coverArtFileExtension #3: only has value for unique cover art`); - - track.coverArtistContribs = contribs; - - album.trackCoverArtFileExtension = 'png'; - XXX_decacheWikiData(); - - t.equal(track.coverArtFileExtension, 'png', - `coverArtFileExtension #4: inherits album trackCoverArtFileExtension (1/2)`); - - track.coverArtFileExtension = 'gif'; - - t.equal(track.coverArtFileExtension, 'gif', - `coverArtFileExtension #5: is own value (1/2)`); - - track.coverArtistContribs = []; - - album.trackCoverArtistContribs = contribs; - XXX_decacheWikiData(); - - t.equal(track.coverArtFileExtension, 'gif', - `coverArtFileExtension #6: is own value (2/2)`); - - track.coverArtFileExtension = null; - - t.equal(track.coverArtFileExtension, 'png', - `coverArtFileExtension #7: inherits album trackCoverArtFileExtension (2/2)`); - - track.disableUniqueCoverArt = true; - - t.equal(track.coverArtFileExtension, null, - `coverArtFileExtension #8: is null if track disables unique cover art`); -}); - -t.test(`Track.date`, t => { - t.plan(3); - - const wikiData = stubWikiData(); - - const {track, album} = stubTrackAndAlbum(wikiData); - - const {XXX_decacheWikiData} = linkAndBindWikiData(wikiData); - - t.equal(track.date, null, - `date #1: defaults to null`); - - album.date = new Date('2012-12-12'); - XXX_decacheWikiData(); - - t.same(track.date, album.date, - `date #2: inherits from album`); - - track.dateFirstReleased = new Date('2009-09-09'); - - t.same(track.date, new Date('2009-09-09'), - `date #3: is own dateFirstReleased`); -}); - -t.test(`Track.featuredInFlashes`, t => { - t.plan(2); - - const wikiData = stubWikiData(); - - const {track} = stubTrackAndAlbum(wikiData, 'track1'); - const {flash: flash1} = stubFlashAndAct(wikiData, 'flash1'); - const {flash: flash2} = stubFlashAndAct(wikiData, 'flash2'); - - const {XXX_decacheWikiData} = linkAndBindWikiData(wikiData); - - t.same(track.featuredInFlashes, [], - `featuredInFlashes #1: defaults to empty array`); - - flash1.featuredTracks = ['track:track1']; - flash2.featuredTracks = ['track:track1']; - XXX_decacheWikiData(); - - t.same(track.featuredInFlashes, [flash1, flash2], - `featuredInFlashes #2: matches flashes' featuredTracks`); -}); - -t.test(`Track.hasUniqueCoverArt`, t => { - t.plan(7); - - const wikiData = stubWikiData(); - - const {track, album} = stubTrackAndAlbum(wikiData); - const {contribs, badContribs} = stubArtistAndContribs(wikiData); - - const {XXX_decacheWikiData} = linkAndBindWikiData(wikiData); - - t.equal(track.hasUniqueCoverArt, false, - `hasUniqueCoverArt #1: defaults to false`); - - album.trackCoverArtistContribs = contribs; - XXX_decacheWikiData(); - - t.equal(track.hasUniqueCoverArt, true, - `hasUniqueCoverArt #2: is true if album specifies trackCoverArtistContribs`); - - track.disableUniqueCoverArt = true; - - t.equal(track.hasUniqueCoverArt, false, - `hasUniqueCoverArt #3: is false if disableUniqueCoverArt is true (1/2)`); - - track.disableUniqueCoverArt = false; - - album.trackCoverArtistContribs = badContribs; - XXX_decacheWikiData(); - - t.equal(track.hasUniqueCoverArt, false, - `hasUniqueCoverArt #4: is false if album's trackCoverArtistContribs resolve empty`); - - track.coverArtistContribs = contribs; - - t.equal(track.hasUniqueCoverArt, true, - `hasUniqueCoverArt #5: is true if track specifies coverArtistContribs`); - - track.disableUniqueCoverArt = true; - - t.equal(track.hasUniqueCoverArt, false, - `hasUniqueCoverArt #6: is false if disableUniqueCoverArt is true (2/2)`); - - track.disableUniqueCoverArt = false; - - track.coverArtistContribs = badContribs; - - t.equal(track.hasUniqueCoverArt, false, - `hasUniqueCoverArt #7: is false if track's coverArtistContribs resolve empty`); -}); - -t.test(`Track.originalReleaseTrack`, t => { - t.plan(3); - - const wikiData = stubWikiData(); - - const {track: track1} = stubTrackAndAlbum(wikiData, 'track1'); - const {track: track2} = stubTrackAndAlbum(wikiData, 'track2'); - - linkAndBindWikiData(wikiData); - - t.equal(track2.originalReleaseTrack, null, - `originalReleaseTrack #1: defaults to null`); - - track2.originalReleaseTrack = 'track:track1'; - - t.equal(track2.originalReleaseTrack, track1, - `originalReleaseTrack #2: is resolved from own value`); - - track2.trackData = []; - - t.equal(track2.originalReleaseTrack, null, - `originalReleaseTrack #3: is null when track missing trackData`); -}); - -t.test(`Track.otherReleases`, t => { - t.plan(6); - - const wikiData = stubWikiData(); - - const {track: track1} = stubTrackAndAlbum(wikiData, 'track1'); - const {track: track2} = stubTrackAndAlbum(wikiData, 'track2'); - const {track: track3} = stubTrackAndAlbum(wikiData, 'track3'); - const {track: track4} = stubTrackAndAlbum(wikiData, 'track4'); - - const {linkWikiDataArrays, XXX_decacheWikiData} = linkAndBindWikiData(wikiData); - - t.same(track1.otherReleases, [], - `otherReleases #1: defaults to empty array`); - - track2.originalReleaseTrack = 'track:track1'; - track3.originalReleaseTrack = 'track:track1'; - track4.originalReleaseTrack = 'track:track1'; - XXX_decacheWikiData(); - - t.same(track1.otherReleases, [track2, track3, track4], - `otherReleases #2: otherReleases of original release are its rereleases`); - - wikiData.trackData = [track1, track3, track2, track4]; - linkWikiDataArrays({bindFind}); - - t.same(track1.otherReleases, [track3, track2, track4], - `otherReleases #3: otherReleases matches trackData order`); - - wikiData.trackData = [track3, track2, track1, track4]; - linkWikiDataArrays({bindFind}); - - t.same(track2.otherReleases, [track1, track3, track4], - `otherReleases #4: otherReleases of rerelease are original track then other rereleases (1/3)`); - - t.same(track3.otherReleases, [track1, track2, track4], - `otherReleases #5: otherReleases of rerelease are original track then other rereleases (2/3)`); - - t.same(track4.otherReleases, [track1, track3, track2], - `otherReleases #6: otherReleases of rerelease are original track then other rereleases (3/3)`); -}); - -t.test(`Track.referencedByTracks`, t => { - t.plan(4); - - const wikiData = stubWikiData(); - - const {track: track1} = stubTrackAndAlbum(wikiData, 'track1'); - const {track: track2} = stubTrackAndAlbum(wikiData, 'track2'); - const {track: track3} = stubTrackAndAlbum(wikiData, 'track3'); - const {track: track4} = stubTrackAndAlbum(wikiData, 'track4'); - - const {XXX_decacheWikiData} = linkAndBindWikiData(wikiData); - - t.same(track1.referencedByTracks, [], - `referencedByTracks #1: defaults to empty array`); - - track2.referencedTracks = ['track:track1']; - track3.referencedTracks = ['track:track1']; - XXX_decacheWikiData(); - - t.same(track1.referencedByTracks, [track2, track3], - `referencedByTracks #2: matches tracks' referencedTracks`); - - track4.sampledTracks = ['track:track1']; - XXX_decacheWikiData(); - - t.same(track1.referencedByTracks, [track2, track3], - `referencedByTracks #3: doesn't match tracks' sampledTracks`); - - track3.originalReleaseTrack = 'track:track2'; - XXX_decacheWikiData(); - - t.same(track1.referencedByTracks, [track2], - `referencedByTracks #4: doesn't include rereleases`); -}); - -t.test(`Track.sampledByTracks`, t => { - t.plan(4); - - const wikiData = stubWikiData(); - - const {track: track1} = stubTrackAndAlbum(wikiData, 'track1'); - const {track: track2} = stubTrackAndAlbum(wikiData, 'track2'); - const {track: track3} = stubTrackAndAlbum(wikiData, 'track3'); - const {track: track4} = stubTrackAndAlbum(wikiData, 'track4'); - - const {XXX_decacheWikiData} = linkAndBindWikiData(wikiData); - - t.same(track1.sampledByTracks, [], - `sampledByTracks #1: defaults to empty array`); - - track2.sampledTracks = ['track:track1']; - track3.sampledTracks = ['track:track1']; - XXX_decacheWikiData(); - - t.same(track1.sampledByTracks, [track2, track3], - `sampledByTracks #2: matches tracks' sampledTracks`); - - track4.referencedTracks = ['track:track1']; - XXX_decacheWikiData(); - - t.same(track1.sampledByTracks, [track2, track3], - `sampledByTracks #3: doesn't match tracks' referencedTracks`); - - track3.originalReleaseTrack = 'track:track2'; - XXX_decacheWikiData(); - - t.same(track1.sampledByTracks, [track2], - `sampledByTracks #4: doesn't include rereleases`); -}); diff --git a/test/unit/data/validators.js b/test/unit/data/validators.js index 3a217d6f..6e37f170 100644 --- a/test/unit/data/validators.js +++ b/test/unit/data/validators.js @@ -17,7 +17,6 @@ import { // Wiki data isColor, - isCommentary, isContentString, isContribution, isContributionList, @@ -152,21 +151,6 @@ t.test('isColor', t => { t.throws(() => isColor('hsl(150deg 30% 60%)'), TypeError); }); -t.test('isCommentary', t => { - t.plan(9); - - // TODO: Test specific error messages. - t.ok(isCommentary(`<i>Toby Fox:</i>\ndogsong.mp3`)); - t.ok(isCommentary(`<i>Toby Fox:</i> (music)\ndogsong.mp3`)); - t.throws(() => isCommentary(`dogsong.mp3\n<i>Toby Fox:</i>\ndogsong.mp3`)); - t.throws(() => isCommentary(`<i>Toby Fox:</i> dogsong.mp3`)); - t.throws(() => isCommentary(`<i>Toby Fox:</i> (music) dogsong.mp3`)); - t.throws(() => isCommentary(`<i>I Have Nothing To Say:</i>`)); - t.throws(() => isCommentary(123)); - t.throws(() => isCommentary(``)); - t.throws(() => isCommentary(`Technically, ah, er:</i>\nCorrect`)); -}); - t.test('isContentString', t => { t.plan(12); @@ -339,7 +323,7 @@ t.test('isName', t => { t.plan(4); t.ok(isName('Dogz 2.0')); t.ok(isName('album:this-track-is-only-named-thusly-to-give-niklink-a-headache')); - t.throws(() => isName('')); + t.ok(() => isName('')); t.throws(() => isName(612)); }); @@ -374,8 +358,8 @@ test(t, 'validateReference', t => { t.ok(typeless('Hopes and Dreams')); t.ok(typeless('track:snowdin-town')); + t.ok(typeless('album:undertale-soundtrack')); t.throws(() => typeless(''), TypeError); - t.throws(() => typeless('album:undertale-soundtrack')); }); test(t, 'validateReferenceList', t => { diff --git a/test/unit/util/html.js b/test/unit/util/html.js index 1652aee2..126e36ff 100644 --- a/test/unit/util/html.js +++ b/test/unit/util/html.js @@ -545,7 +545,7 @@ t.test(`html.template`, t => { slots.slot1, slots.slot2, slots.slot3, - `(length: ${slots.slot4.length})`, + `(is null: ${slots.slot4 === null})`, ].join(' ')); }, }); @@ -557,7 +557,7 @@ t.test(`html.template`, t => { slot4: '', }); - t.equal(template3.toString(), `<span>123 0 false (length: 0)</span>`); + t.equal(template3.toString(), `<span>123 0 false (is null: true)</span>`); }); t.test(`Template - description errors`, t => { |