diff --git a/.changeset/dirty-teachers-hug.md b/.changeset/dirty-teachers-hug.md new file mode 100644 index 000000000000..97aacb1d7fd9 --- /dev/null +++ b/.changeset/dirty-teachers-hug.md @@ -0,0 +1,5 @@ +--- +'create-svelte': patch +--- + +Added the option to add Vitest to new projects diff --git a/packages/create-svelte/bin.js b/packages/create-svelte/bin.js index 94c3dc529d9e..32db8d6e08e9 100755 --- a/packages/create-svelte/bin.js +++ b/packages/create-svelte/bin.js @@ -110,6 +110,14 @@ async function main() { initial: false, active: 'Yes', inactive: 'No' + }, + { + type: 'toggle', + name: 'vitest', + message: 'Add Vitest for unit testing?', + initial: false, + active: 'Yes', + inactive: 'No' } ], { @@ -150,6 +158,11 @@ async function main() { console.log(cyan(' https://playwright.dev')); } + if (options.vitest) { + console.log(bold('✔ Vitest')); + console.log(cyan(' https://vitest.dev')); + } + console.log('\nInstall community-maintained integrations:'); console.log(cyan(' https://github.com/svelte-add/svelte-adders')); diff --git a/packages/create-svelte/scripts/update-template-repo-contents.js b/packages/create-svelte/scripts/update-template-repo-contents.js index 60f3cbee84e0..1eb7a07dc7db 100644 --- a/packages/create-svelte/scripts/update-template-repo-contents.js +++ b/packages/create-svelte/scripts/update-template-repo-contents.js @@ -19,7 +19,8 @@ await create(repo, { eslint: false, types: 'checkjs', prettier: true, - playwright: false + playwright: false, + vitest: false }); // Remove the Sverdle from the template because it doesn't work within Stackblitz (cookies not set) diff --git a/packages/create-svelte/shared/+default+vitest/src/routes/sverdle/game.test.ts b/packages/create-svelte/shared/+default+vitest/src/routes/sverdle/game.test.ts new file mode 100644 index 000000000000..99028b6f561e --- /dev/null +++ b/packages/create-svelte/shared/+default+vitest/src/routes/sverdle/game.test.ts @@ -0,0 +1,9 @@ +import { describe, it, expect } from 'vitest'; +import { Game } from './game'; + +describe('game test', () => { + it('returns true when a valid word is entered', () => { + const game = new Game(); + expect(game.enter('zorro'.split(''))).toBe(true); + }); +}); diff --git a/packages/create-svelte/shared/+playwright+skeletonlib/tests/test.ts b/packages/create-svelte/shared/+playwright+skeletonlib/tests/test.ts new file mode 100644 index 000000000000..4e579377eed9 --- /dev/null +++ b/packages/create-svelte/shared/+playwright+skeletonlib/tests/test.ts @@ -0,0 +1,6 @@ +import { expect, test } from '@playwright/test'; + +test('index page has expected h1', async ({ page }) => { + await page.goto('/'); + expect(await page.textContent('h1')).toBe('Welcome to SvelteKit'); +}); diff --git a/packages/create-svelte/shared/+playwright+typescript/playwright.config.ts b/packages/create-svelte/shared/+playwright+typescript/playwright.config.ts index 6ad3a7faa279..2d5a65aee723 100644 --- a/packages/create-svelte/shared/+playwright+typescript/playwright.config.ts +++ b/packages/create-svelte/shared/+playwright+typescript/playwright.config.ts @@ -4,7 +4,8 @@ const config: PlaywrightTestConfig = { webServer: { command: 'npm run build && npm run preview', port: 4173 - } + }, + testMatch: 'tests/**/.*(test|spec).(js|ts)' }; export default config; diff --git a/packages/create-svelte/shared/+playwright-typescript/playwright.config.js b/packages/create-svelte/shared/+playwright-typescript/playwright.config.js index ea17df6ed35d..99422c1de342 100644 --- a/packages/create-svelte/shared/+playwright-typescript/playwright.config.js +++ b/packages/create-svelte/shared/+playwright-typescript/playwright.config.js @@ -3,7 +3,8 @@ const config = { webServer: { command: 'npm run build && npm run preview', port: 4173 - } + }, + testMatch: 'tests/**/.*(test|spec).(js|ts)' }; export default config; diff --git a/packages/create-svelte/shared/+vitest+skeleton/src/index.test.ts b/packages/create-svelte/shared/+vitest+skeleton/src/index.test.ts new file mode 100644 index 000000000000..e07cbbd72507 --- /dev/null +++ b/packages/create-svelte/shared/+vitest+skeleton/src/index.test.ts @@ -0,0 +1,7 @@ +import { describe, it, expect } from 'vitest'; + +describe('sum test', () => { + it('adds 1 + 2 to equal 3', () => { + expect(1 + 2).toBe(3); + }); +}); diff --git a/packages/create-svelte/shared/+vitest+skeletonlib/src/index.test.ts b/packages/create-svelte/shared/+vitest+skeletonlib/src/index.test.ts new file mode 100644 index 000000000000..e07cbbd72507 --- /dev/null +++ b/packages/create-svelte/shared/+vitest+skeletonlib/src/index.test.ts @@ -0,0 +1,7 @@ +import { describe, it, expect } from 'vitest'; + +describe('sum test', () => { + it('adds 1 + 2 to equal 3', () => { + expect(1 + 2).toBe(3); + }); +}); diff --git a/packages/create-svelte/shared/+vitest/package.json b/packages/create-svelte/shared/+vitest/package.json new file mode 100644 index 000000000000..fea2892af5e3 --- /dev/null +++ b/packages/create-svelte/shared/+vitest/package.json @@ -0,0 +1,8 @@ +{ + "devDependencies": { + "vitest": "^0.23.4" + }, + "scripts": { + "test:unit": "vitest" + } +} diff --git a/packages/create-svelte/shared/+vitest/vite.config.js b/packages/create-svelte/shared/+vitest/vite.config.js new file mode 100644 index 000000000000..652979fb41bc --- /dev/null +++ b/packages/create-svelte/shared/+vitest/vite.config.js @@ -0,0 +1,11 @@ +import { sveltekit } from '@sveltejs/kit/vite'; + +/** @type {import('vite').UserConfig} */ +const config = { + plugins: [sveltekit()], + test: { + include: ['src/**/*.{test,spec}.{js,ts}'] + } +}; + +export default config; diff --git a/packages/create-svelte/shared/vite.config.ts b/packages/create-svelte/shared/vite.config.js similarity index 67% rename from packages/create-svelte/shared/vite.config.ts rename to packages/create-svelte/shared/vite.config.js index 60ef89c74357..8747050534d8 100644 --- a/packages/create-svelte/shared/vite.config.ts +++ b/packages/create-svelte/shared/vite.config.js @@ -1,8 +1,7 @@ import { sveltekit } from '@sveltejs/kit/vite'; -import type { UserConfig } from 'vite'; /** @type {import('vite').UserConfig} */ -const config: UserConfig = { +const config = { plugins: [sveltekit()] }; diff --git a/packages/create-svelte/templates/default/src/routes/sverdle/+page.server.ts b/packages/create-svelte/templates/default/src/routes/sverdle/+page.server.ts index b95dbde04d00..9e8075993ab6 100644 --- a/packages/create-svelte/templates/default/src/routes/sverdle/+page.server.ts +++ b/packages/create-svelte/templates/default/src/routes/sverdle/+page.server.ts @@ -1,5 +1,5 @@ import { invalid } from '@sveltejs/kit'; -import { words, allowed } from './words.server'; +import { Game } from './game'; import type { PageServerLoad, Actions } from './$types'; /** @type {import('./$types').PageServerLoad} */ @@ -69,79 +69,3 @@ export const actions: Actions = { cookies.delete('sverdle'); } }; - -class Game { - index: number; - guesses: string[]; - answers: string[]; - answer: string; - - /** - * Create a game object from the player's cookie, or initialise a new game - * @param {string | undefined} serialized - */ - constructor(serialized: string | undefined) { - if (serialized) { - const [index, guesses, answers] = serialized.split('-'); - - this.index = +index; - this.guesses = guesses ? guesses.split(' ') : []; - this.answers = answers ? answers.split(' ') : []; - } else { - this.index = Math.floor(Math.random() * words.length); - this.guesses = ['', '', '', '', '', '']; - this.answers = /** @type {string[]} */ [] /***/; - } - - this.answer = words[this.index]; - } - - /** - * Update game state based on a guess of a five-letter word. Returns - * true if the guess was valid, false otherwise - * @param {string[]} letters - */ - enter(letters: string[]) { - const word = letters.join(''); - const valid = allowed.has(word); - - if (!valid) return false; - - this.guesses[this.answers.length] = word; - - const available = Array.from(this.answer); - const answer = Array(5).fill('_'); - - // first, find exact matches - for (let i = 0; i < 5; i += 1) { - if (letters[i] === available[i]) { - answer[i] = 'x'; - available[i] = ' '; - } - } - - // then find close matches (this has to happen - // in a second step, otherwise an early close - // match can prevent a later exact match) - for (let i = 0; i < 5; i += 1) { - if (answer[i] === '_') { - const index = available.indexOf(letters[i]); - if (index !== -1) { - answer[i] = 'c'; - available[index] = ' '; - } - } - } - - this.answers.push(answer.join('')); - - return true; - } - - /** - * Serialize game state so it can be set as a cookie - */ - toString() { - return `${this.index}-${this.guesses.join(' ')}-${this.answers.join(' ')}`; - } -} diff --git a/packages/create-svelte/templates/default/src/routes/sverdle/game.ts b/packages/create-svelte/templates/default/src/routes/sverdle/game.ts new file mode 100644 index 000000000000..d5cbe932e4cb --- /dev/null +++ b/packages/create-svelte/templates/default/src/routes/sverdle/game.ts @@ -0,0 +1,77 @@ +import { words, allowed } from './words.server'; + +export class Game { + index: number; + guesses: string[]; + answers: string[]; + answer: string; + + /** + * Create a game object from the player's cookie, or initialise a new game + * @param {string | undefined} serialized + */ + constructor(serialized: string | undefined = undefined) { + if (serialized) { + const [index, guesses, answers] = serialized.split('-'); + + this.index = +index; + this.guesses = guesses ? guesses.split(' ') : []; + this.answers = answers ? answers.split(' ') : []; + } else { + this.index = Math.floor(Math.random() * words.length); + this.guesses = ['', '', '', '', '', '']; + this.answers = /** @type {string[]} */ [] /***/; + } + + this.answer = words[this.index]; + } + + /** + * Update game state based on a guess of a five-letter word. Returns + * true if the guess was valid, false otherwise + * @param {string[]} letters + */ + enter(letters: string[]) { + const word = letters.join(''); + const valid = allowed.has(word); + + if (!valid) return false; + + this.guesses[this.answers.length] = word; + + const available = Array.from(this.answer); + const answer = Array(5).fill('_'); + + // first, find exact matches + for (let i = 0; i < 5; i += 1) { + if (letters[i] === available[i]) { + answer[i] = 'x'; + available[i] = ' '; + } + } + + // then find close matches (this has to happen + // in a second step, otherwise an early close + // match can prevent a later exact match) + for (let i = 0; i < 5; i += 1) { + if (answer[i] === '_') { + const index = available.indexOf(letters[i]); + if (index !== -1) { + answer[i] = 'c'; + available[index] = ' '; + } + } + } + + this.answers.push(answer.join('')); + + return true; + } + + /** + * Serialize game state so it can be set as a cookie + */ + toString() { + return `${this.index}-${this.guesses.join(' ')}-${this.answers.join(' ')}`; + } +} diff --git a/packages/create-svelte/types/internal.d.ts b/packages/create-svelte/types/internal.d.ts index 8571807655d2..612d1cd3ceba 100644 --- a/packages/create-svelte/types/internal.d.ts +++ b/packages/create-svelte/types/internal.d.ts @@ -5,6 +5,7 @@ export type Options = { prettier: boolean; eslint: boolean; playwright: boolean; + vitest: boolean; }; export type File = { @@ -18,6 +19,7 @@ export type Condition = | 'typescript' | 'checkjs' | 'playwright' + | 'vitest' | 'skeleton' | 'default' | 'skeletonlib';