diff --git a/src/options.spec.ts b/src/options.spec.ts new file mode 100644 index 000000000..f79902508 --- /dev/null +++ b/src/options.spec.ts @@ -0,0 +1,18 @@ +import { describe, expect, it } from 'vitest' +import { resolveOptions } from './options' +import { mockWarn } from '../tests/vitest-mock-warn' + +describe('options', () => { + mockWarn() + it('ensure starting dots in extensions', () => { + expect( + resolveOptions({ + extensions: ['vue', '.ts'], + }) + ).toMatchObject({ + extensions: ['.vue', '.ts'], + }) + + expect('Invalid extension "vue"').toHaveBeenWarned() + }) +}) diff --git a/src/options.ts b/src/options.ts index 47ca9788b..8680936f8 100644 --- a/src/options.ts +++ b/src/options.ts @@ -132,9 +132,22 @@ export function resolveOptions(options: Options): ResolvedOptions { })) if (options.extensions) { - // sort extensions by length to ensure that the longest one is used first - // e.g. ['.vue', '.page.vue'] -> ['.page.vue', '.vue'] as both would match and order matters - options.extensions.sort((a, b) => b.length - a.length) + options.extensions = options.extensions + // ensure that extensions start with a dot or warn the user + // this is needed when filtering the files with the pattern .{vue,js,ts} + // in src/index.ts + .map((ext) => { + if (!ext.startsWith('.')) { + console.warn( + `[unplugin-vue-router]: Invalid extension "${ext}". Extensions must start with a dot.` + ) + return '.' + ext + } + return ext + }) + // sort extensions by length to ensure that the longest one is used first + // e.g. ['.vue', '.page.vue'] -> ['.page.vue', '.vue'] as both would match and order matters + .sort((a, b) => b.length - a.length) } return { diff --git a/tests/vitest-mock-warn.ts b/tests/vitest-mock-warn.ts new file mode 100644 index 000000000..f75e6496f --- /dev/null +++ b/tests/vitest-mock-warn.ts @@ -0,0 +1,118 @@ +// https://github.com/posva/jest-mock-warn/blob/master/src/index.js + +import { afterEach, beforeEach, expect, SpyInstance, vi } from 'vitest' + +export function mockWarn() { + expect.extend({ + toHaveBeenWarned(received: string | RegExp) { + asserted.set(received.toString(), received) + const passed = warn.mock.calls.some((args) => + typeof received === 'string' + ? args[0].indexOf(received) > -1 + : received.test(args[0]) + ) + if (passed) { + return { + pass: true, + message: () => `expected "${received}" not to have been warned.`, + } + } else { + const msgs = warn.mock.calls.map((args) => args[0]).join('\n - ') + return { + pass: false, + message: () => + `expected "${received}" to have been warned.\n\nActual messages:\n\n - ${msgs}`, + } + } + }, + + toHaveBeenWarnedLast(received: string | RegExp) { + asserted.set(received.toString(), received) + const lastCall = warn.mock.calls[warn.mock.calls.length - 1][0] + const passed = + typeof received === 'string' + ? lastCall.indexOf(received) > -1 + : received.test(lastCall) + if (passed) { + return { + pass: true, + message: () => `expected "${received}" not to have been warned last.`, + } + } else { + const msgs = warn.mock.calls.map((args) => args[0]).join('\n - ') + return { + pass: false, + message: () => + `expected "${received}" to have been warned last.\n\nActual messages:\n\n - ${msgs}`, + } + } + }, + + toHaveBeenWarnedTimes(received: string | RegExp, n: number) { + asserted.set(received.toString(), received) + let found = 0 + warn.mock.calls.forEach((args) => { + const isFound = + typeof received === 'string' + ? args[0].indexOf(received) > -1 + : received.test(args[0]) + if (isFound) { + found++ + } + }) + + if (found === n) { + return { + pass: true, + message: () => + `expected "${received}" to have been warned ${n} times.`, + } + } else { + return { + pass: false, + message: () => + `expected "${received}" to have been warned ${n} times but got ${found}.`, + } + } + }, + }) + + let warn: SpyInstance + const asserted = new Map() + + beforeEach(() => { + asserted.clear() + warn = vi.spyOn(console, 'warn') + warn.mockImplementation(() => {}) + }) + + afterEach(() => { + const assertedArray = Array.from(asserted) + const nonAssertedWarnings = warn.mock.calls + .map((args) => args[0]) + .filter((received) => { + return !assertedArray.some(([key, assertedMsg]) => { + return typeof assertedMsg === 'string' + ? received.indexOf(assertedMsg) > -1 + : assertedMsg.test(received) + }) + }) + warn.mockRestore() + if (nonAssertedWarnings.length) { + nonAssertedWarnings.forEach((warning) => { + console.warn(warning) + }) + throw new Error(`test case threw unexpected warnings.`) + } + }) +} + +declare global { + namespace Vi { + interface JestAssertion { + toHaveBeenWarned(): void + toHaveBeenWarnedLast(): void + toHaveBeenWarnedTimes(n: number): void + } + } +}