Skip to content
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
4d02345
test with tailwindcss & prettier
jycouet Sep 17, 2025
ea217b5
switch things around!
jycouet Sep 17, 2025
9114c74
rmv ps-tree
jycouet Sep 17, 2025
5b4f578
only kit for all addons (like before^^)
jycouet Sep 17, 2025
3244ffb
naming
jycouet Sep 17, 2025
237e39a
@tailwindcss/oxide test
jycouet Sep 18, 2025
46affd2
back
jycouet Sep 18, 2025
cf15192
test & pnpm
jycouet Sep 18, 2025
b6ecec8
naming
jycouet Sep 18, 2025
807016d
feat(cli): pnpm config will now be stored in pnpm-workspace.yaml (#718)
jycouet Sep 24, 2025
20c8a88
Merge branch 'main' of github.com:sveltejs/cli into perf/tests-v7
jycouet Sep 24, 2025
142bde7
.
jycouet Sep 24, 2025
0535e99
tsc
jycouet Sep 24, 2025
cf71e3f
it's internal stuff
jycouet Sep 26, 2025
8d0c7f2
Merge branch 'main' of github.com:sveltejs/cli into perf/tests-v7
jycouet Sep 28, 2025
5ffc801
Merge branch 'main' of github.com:sveltejs/cli into perf/tests-v7
jycouet Sep 29, 2025
32dceed
using same strategy for community-addon-template
jycouet Sep 29, 2025
1509341
linting power
jycouet Sep 29, 2025
a96c339
remove unused
AdrianGonz97 Oct 2, 2025
95b1826
dedupe
AdrianGonz97 Oct 3, 2025
fe7f49c
tweak
AdrianGonz97 Oct 3, 2025
64304e3
keep npm where it's needed
jycouet Oct 3, 2025
fefee20
tinyexec: x to exec
jycouet Oct 3, 2025
0b83c1b
Flavor to AddonTestCase
jycouet Oct 3, 2025
c74d103
Update packages/addons/_tests/_setup/suite.ts
jycouet Oct 3, 2025
d2bc07a
oupy yes full rename to AddonTestCase
jycouet Oct 3, 2025
c97827d
improve typing
jycouet Oct 3, 2025
2acf0b7
tweak name
AdrianGonz97 Oct 5, 2025
94984d5
change `ctx.run` to `ctx.cwd`
AdrianGonz97 Oct 5, 2025
5e9639d
redundant `installCommand`
AdrianGonz97 Oct 5, 2025
80db5be
missing encoding
AdrianGonz97 Oct 5, 2025
fee50f5
`beforeBuild` no longer necessary since `installCommand` moved
AdrianGonz97 Oct 5, 2025
74af92a
last bit
AdrianGonz97 Oct 5, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/nasty-towns-crash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'sv': patch
---

fix(tailwindcss): add `@tailwindcss/oxide` to approve-builds in `pnpm`
5 changes: 5 additions & 0 deletions .changeset/solid-dragons-trade.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'sv': patch
---

feat(cli): pnpm config will now be stored in `pnpm-workspace.yaml` (e.g. `onlyBuiltDependencies`)
42 changes: 24 additions & 18 deletions community-addon-template/tests/custom-addon.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,31 @@ import { fixture, setupTest } from './setup/suite.js';
import addon from '../src/index.js';

const id = addon.id;
const { test, variants, prepareServer } = setupTest({ [id]: addon });
const { test, addonTestCases, prepareServer } = setupTest(
{ [id]: addon },
{ kinds: [{ type: 'default', options: { [id]: { demo: true } } }] }
);

test.concurrent.for(variants)('demo - %s', async (variant, { page, ...ctx }) => {
const cwd = await ctx.run(variant, { [id]: { demo: true } });
test.concurrent.for(addonTestCases)(
'community-addon $variant',
async (addonTestCase, { page, ...ctx }) => {
const cwd = ctx.run(addonTestCase);

// ...add files
if (variant.startsWith('kit')) {
const target = path.resolve(cwd, 'src', 'routes', '+page.svelte');
fixture({ name: '+page.svelte', target });
} else {
const target = path.resolve(cwd, 'src', 'App.svelte');
fixture({ name: 'App.svelte', target });
}
// ...add files
if (addonTestCase.variant.startsWith('kit')) {
const target = path.resolve(cwd, 'src', 'routes', '+page.svelte');
fixture({ name: '+page.svelte', target });
} else {
const target = path.resolve(cwd, 'src', 'App.svelte');
fixture({ name: 'App.svelte', target });
}

const { close } = await prepareServer({ cwd, page });
// kill server process when we're done
ctx.onTestFinished(async () => await close());
const { close } = await prepareServer({ cwd, page });
// kill server process when we're done
ctx.onTestFinished(async () => await close());

// expectations
const textContent = await page.getByTestId('demo').textContent();
expect(textContent).toContain('This is a text file made by the Community Addon Template demo!');
});
// expectations
const textContent = await page.getByTestId('demo').textContent();
expect(textContent).toContain('This is a text file made by the Community Addon Template demo!');
}
);
2 changes: 1 addition & 1 deletion community-addon-template/tests/setup/global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { fileURLToPath } from 'node:url';
import { setup, type ProjectVariant } from 'sv/testing';
import type { TestProject } from 'vitest/node';

const variants: ProjectVariant[] = ['kit-js', 'kit-ts', 'vite-js', 'vite-ts'];
const TEST_DIR = fileURLToPath(new URL('../../.test-output/', import.meta.url));
const variants: ProjectVariant[] = ['kit-js', 'kit-ts', 'vite-js', 'vite-ts'];

export default async function ({ provide }: TestProject) {
// global setup (e.g. spin up docker containers)
Expand Down
138 changes: 96 additions & 42 deletions community-addon-template/tests/setup/suite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import { execSync } from 'node:child_process';
import * as vitest from 'vitest';
import { installAddon, type AddonMap, type OptionMap } from 'sv';
import {
addPnpmBuildDependencies,
createProject,
startPreview,
addPnpmBuildDependencies,
type CreateProject,
type ProjectVariant
} from 'sv/testing';
import { chromium, type Browser, type Page } from '@playwright/test';
import { chromium, type Browser, type BrowserContext, type Page } from '@playwright/test';
import { fileURLToPath } from 'node:url';

const cwd = vitest.inject('testDir');
Expand All @@ -21,24 +21,50 @@ const SETUP_DIR = fileURLToPath(new URL('.', import.meta.url));

type Fixtures<Addons extends AddonMap> = {
page: Page;
run(variant: ProjectVariant, options: OptionMap<Addons>): Promise<string>;
run(addonTestCase: Flavor<Addons>): string;
};

export function setupTest<Addons extends AddonMap>(addons: Addons) {
let create: CreateProject;
let browser: Browser;
type Flavor<Addons extends AddonMap> = {
variant: ProjectVariant;
kind: { type: string; options: OptionMap<Addons> };
};

export function setupTest<Addons extends AddonMap>(
addons: Addons,
options?: {
kinds: Array<Flavor<Addons>['kind']>;
filter?: (addonTestCase: Flavor<Addons>) => boolean;
browser?: boolean;
}
) {
const test = vitest.test.extend<Fixtures<Addons>>({} as any);

vitest.beforeAll(async () => {
browser = await chromium.launch();
return async () => {
await browser.close();
};
});
const withBrowser = options?.browser ?? true;

let create: CreateProject;
let browser: Browser;

if (withBrowser) {
vitest.beforeAll(async () => {
browser = await chromium.launch();
return async () => {
await browser.close();
};
});
}

vitest.beforeAll(({ name }) => {
const testName = path.parse(name).name.replace('.test', '');
const addonTestCases: Array<Flavor<Addons>> = [];
for (const kind of options?.kinds ?? []) {
for (const variant of variants) {
const addonTestCase = { variant, kind };
if (!options?.filter || options?.filter?.(addonTestCase)) {
addonTestCases.push(addonTestCase);
}
}
}
let testName: string;
vitest.beforeAll(async ({ name }) => {
testName = path.dirname(name).split('/').at(-1)!;

// constructs a builder for create test projects
create = createProject({ cwd, templatesDir, testName });
Expand All @@ -58,61 +84,89 @@ export function setupTest<Addons extends AddonMap>(addons: Addons) {
private: true
})
);
});

// runs before each test case
vitest.beforeEach<Fixtures<Addons>>(async (ctx) => {
const browserCtx = await browser.newContext();
ctx.page = await browserCtx.newPage();
ctx.run = async (variant, options) => {
const cwd = create({ testId: ctx.task.id, variant });
for (const { variant, kind } of addonTestCases) {
const cwd = create({ testId: `${kind.type}-${variant}`, variant });

// test metadata
const metaPath = path.resolve(cwd, 'meta.json');
fs.writeFileSync(metaPath, JSON.stringify({ variant, options }, null, '\t'), 'utf8');
fs.writeFileSync(metaPath, JSON.stringify({ variant, kind }, null, '\t'), 'utf8');

// run addon
const { pnpmBuildDependencies } = await installAddon({
cwd,
addons,
options,
options: kind.options,
packageManager: 'pnpm'
});
addPnpmBuildDependencies(cwd, 'pnpm', ['esbuild', ...pnpmBuildDependencies]);
await addPnpmBuildDependencies(cwd, 'pnpm', ['esbuild', ...pnpmBuildDependencies]);
}

return cwd;
execSync('pnpm install', { cwd: path.resolve(cwd, testName), stdio: 'pipe' });
});

// runs before each test case
vitest.beforeEach<Fixtures<Addons>>(async (ctx) => {
let browserCtx: BrowserContext;
if (withBrowser) {
browserCtx = await browser.newContext();
ctx.page = await browserCtx.newPage();
}

ctx.run = (addonTestCase) => {
return path.join(cwd, testName, `${addonTestCase.kind.type}-${addonTestCase.variant}`);
};

return async () => {
await browserCtx.close();
if (withBrowser) {
await browserCtx.close();
}
// ...other tear downs
};
});

return {
test,
variants,
prepareServer
};
return { test, addonTestCases, prepareServer };
}

/**
* Installs dependencies, builds the project, and spins up the preview server
*/
async function prepareServer({ cwd, page }: { cwd: string; page: Page }) {
type PrepareServerOptions = {
cwd: string;
page: Page;
installCommand?: string;
beforeBuild?: () => Promise<any> | any;
buildCommand?: string;
previewCommand?: string;
};
// installs dependencies, builds the project, and spins up the preview server
async function prepareServer({
cwd,
page,
installCommand, // should happen in the beforeAll hook
beforeBuild,
buildCommand = 'pnpm build',
previewCommand = 'pnpm preview'
}: PrepareServerOptions) {
// install deps
execSync('pnpm install --no-frozen-lockfile', { cwd, stdio: 'pipe' });
if (installCommand) execSync(installCommand, { cwd, stdio: 'pipe' });

// ...do commands and any other extra stuff
await beforeBuild?.();

// build project
execSync('npm run build', { cwd, stdio: 'pipe' });
if (buildCommand) execSync(buildCommand, { cwd, stdio: 'pipe' });

// start preview server
const { url, close } = await startPreview({ cwd, command: previewCommand });

// start preview server `vite preview`
const { url, close } = await startPreview({ cwd });
// increases timeout as 30s is not always enough when running the full suite
page.setDefaultNavigationTimeout(60_000);

// navigate to the page
await page.goto(url);
try {
// navigate to the page
await page.goto(url);
} catch (e) {
// cleanup in the instance of a timeout
await close();
throw e;
}

return { url, close };
}
Expand Down
7 changes: 1 addition & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,5 @@
"typescript-eslint": "^8.34.1",
"vitest": "4.0.0-beta.6"
},
"packageManager": "[email protected]",
"pnpm": {
"onlyBuiltDependencies": [
"esbuild"
]
}
"packageManager": "[email protected]"
}
Loading
Loading