diff --git a/.gitignore b/.gitignore index ac7211b..3bb86b6 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,6 @@ node_modules !.env.example vite.config.js.timestamp-* vite.config.ts.timestamp-* +.cache +test-results +playwright-report diff --git a/package.json b/package.json index a114f26..73e3bd8 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,8 @@ "svelte": "^3.57.0 || ^4.0.0" }, "devDependencies": { - "@playwright/test": "^1.31.2", + "@playwright/experimental-ct-svelte": "^1.35.1", + "@playwright/test": "^1.35.1", "@sveltejs/adapter-auto": "^2.0.0", "@sveltejs/kit": "^1.20.5", "@sveltejs/package": "^2.1.0", diff --git a/playwright.config.ts b/playwright.config.ts index 80731be..6c8f4aa 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -1,11 +1,23 @@ -import type { PlaywrightTestConfig } from '@playwright/test'; +import { defineConfig, devices } from '@playwright/experimental-ct-svelte'; -const config: PlaywrightTestConfig = { - webServer: { - command: 'npm run build && npm run preview', - port: 4173 +export default defineConfig({ + testDir: './tests/components', + timeout: 10 * 1000, + fullyParallel: true, + forbidOnly: !!process.env.CI, + retries: process.env.CI ? 2 : 0, + workers: process.env.CI ? 1 : undefined, + reporter: process.env.CI ? [['github'], ['html']] : [['html', { open: 'never' }]], + use: { + trace: 'retain-on-failure', + video: 'retain-on-failure', + ctPort: 3100, + ctTemplateDir: './tests/components/boilerplate' }, - testDir: 'tests' -}; - -export default config; + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] } + } + ] +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 023fd70..187c9c3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,4 +1,4 @@ -lockfileVersion: '6.1' +lockfileVersion: '6.0' settings: autoInstallPeers: true @@ -10,9 +10,12 @@ dependencies: version: 3.1.0(svelte@4.0.0) devDependencies: + '@playwright/experimental-ct-svelte': + specifier: ^1.35.1 + version: 1.35.1(svelte@4.0.0)(vite@4.2.0) '@playwright/test': - specifier: ^1.31.2 - version: 1.31.2 + specifier: ^1.35.1 + version: 1.35.1 '@sveltejs/adapter-auto': specifier: ^2.0.0 version: 2.0.0(@sveltejs/kit@1.20.5) @@ -396,13 +399,48 @@ packages: fastq: 1.15.0 dev: true - /@playwright/test@1.31.2: - resolution: {integrity: sha512-BYVutxDI4JeZKV1+ups6dt5WiqKhjBtIYowyZIJ3kBDmJgsuPKsqqKNIMFbUePLSCmp2cZu+BDL427RcNKTRYw==} - engines: {node: '>=14'} + /@playwright/experimental-ct-core@1.35.1: + resolution: {integrity: sha512-NSoUf6JDLeZFy0HiENwA1GkIwZHvg5KrygnZknwWs7O8yksYLsmiuMb09sf2zsZmfYgVen401SNgf3KfekbweA==} + engines: {node: '>=16'} + hasBin: true + dependencies: + '@playwright/test': 1.35.1 + vite: 4.3.9 + transitivePeerDependencies: + - '@types/node' + - less + - sass + - stylus + - sugarss + - terser + dev: true + + /@playwright/experimental-ct-svelte@1.35.1(svelte@4.0.0)(vite@4.2.0): + resolution: {integrity: sha512-7CV6pXyZMX9IQl0+J9+eNIv1hZj23z9hMA0GHZr7EubkbJvneIB2HsMKgEGxgW/KSGRqPMBNResfl2ShmHgHHQ==} + engines: {node: '>=16'} + hasBin: true + dependencies: + '@playwright/experimental-ct-core': 1.35.1 + '@sveltejs/vite-plugin-svelte': 2.4.2(svelte@4.0.0)(vite@4.2.0) + transitivePeerDependencies: + - '@types/node' + - less + - sass + - stylus + - sugarss + - supports-color + - svelte + - terser + - vite + dev: true + + /@playwright/test@1.35.1: + resolution: {integrity: sha512-b5YoFe6J9exsMYg0pQAobNDR85T1nLumUYgUTtKm4d21iX2L7WqKq9dW8NGJ+2vX0etZd+Y7UeuqsxDXm9+5ZA==} + engines: {node: '>=16'} hasBin: true dependencies: '@types/node': 18.15.3 - playwright-core: 1.31.2 + playwright-core: 1.35.1 optionalDependencies: fsevents: 2.3.2 dev: true @@ -1896,9 +1934,9 @@ packages: pathe: 1.1.0 dev: true - /playwright-core@1.31.2: - resolution: {integrity: sha512-a1dFgCNQw4vCsG7bnojZjDnPewZcw7tZUNFN0ZkcLYKj+mPmXvg4MpaaKZ5SgqPsOmqIf2YsVRkgqiRDxD+fDQ==} - engines: {node: '>=14'} + /playwright-core@1.35.1: + resolution: {integrity: sha512-pNXb6CQ7OqmGDRspEjlxE49w+4YtR6a3X6mT1hZXeJHWmsEz7SunmvZeiG/+y1yyMZdHnnn73WKYdtV1er0Xyg==} + engines: {node: '>=16'} hasBin: true dev: true @@ -2120,6 +2158,14 @@ packages: fsevents: 2.3.2 dev: true + /rollup@3.25.3: + resolution: {integrity: sha512-ZT279hx8gszBj9uy5FfhoG4bZx8c+0A1sbqtr7Q3KNWIizpTdDEPZbV2xcbvHsnFp4MavCQYZyzApJ+virB8Yw==} + engines: {node: '>=14.18.0', npm: '>=8.0.0'} + hasBin: true + optionalDependencies: + fsevents: 2.3.2 + dev: true + /run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} dependencies: @@ -2622,6 +2668,38 @@ packages: fsevents: 2.3.2 dev: true + /vite@4.3.9: + resolution: {integrity: sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + esbuild: 0.17.12 + postcss: 8.4.24 + rollup: 3.25.3 + optionalDependencies: + fsevents: 2.3.2 + dev: true + /vitefu@0.2.4(vite@4.2.0): resolution: {integrity: sha512-fanAXjSaf9xXtOOeno8wZXIhgia+CZury481LsDaV++lSvcU2R9Ch2bPh3PYFyoHW+w9LqAeYRISVQjUIew14g==} peerDependencies: diff --git a/tests/components/boilerplate/App.svelte b/tests/components/boilerplate/App.svelte new file mode 100644 index 0000000..8254fcd --- /dev/null +++ b/tests/components/boilerplate/App.svelte @@ -0,0 +1,14 @@ + + + diff --git a/tests/components/boilerplate/index.html b/tests/components/boilerplate/index.html new file mode 100644 index 0000000..74896d0 --- /dev/null +++ b/tests/components/boilerplate/index.html @@ -0,0 +1,6 @@ + + +
+ + + diff --git a/tests/components/boilerplate/index.ts b/tests/components/boilerplate/index.ts new file mode 100644 index 0000000..cee3b6f --- /dev/null +++ b/tests/components/boilerplate/index.ts @@ -0,0 +1,2 @@ +// Apply theme here, add anything your component needs at runtime here. +// import '../src/common.css'; diff --git a/tests/components/boilerplate/types.ts b/tests/components/boilerplate/types.ts new file mode 100644 index 0000000..df02ce6 --- /dev/null +++ b/tests/components/boilerplate/types.ts @@ -0,0 +1,5 @@ +import type toast from '$lib'; +import type { test } from '@playwright/experimental-ct-svelte'; + +export type Mount = Parameters[1]>['0']['mount']; +export type ToastProps = Parameters; diff --git a/tests/components/direction.spec.ts b/tests/components/direction.spec.ts new file mode 100644 index 0000000..274170e --- /dev/null +++ b/tests/components/direction.spec.ts @@ -0,0 +1,112 @@ +import { expect, test } from '@playwright/experimental-ct-svelte'; +import App from './boilerplate/App.svelte'; + +test.use({ viewport: { width: 500, height: 500 } }); + +test.describe('LTR', () => { + test.beforeEach(async ({ page }) => { + await page.evaluate(() => { + document.documentElement.lang = 'en'; + document.documentElement.dir = 'ltr'; + }); + }); + + test('top-start', async ({ mount }) => { + const component = await mount(App, { + props: { toastProps: ['Hello world!', { position: 'top-start' }] } + }); + + const toast = component.getByRole('status'); + // toast should be top left + const toastRect = await toast.boundingBox(); + expect(toastRect?.x).toBeLessThan(150); + }); + + test('top-end', async ({ mount }) => { + const component = await mount(App, { + props: { toastProps: ['Hello world!', { position: 'top-end' }] } + }); + + const toast = component.getByRole('status'); + // toast should be top right + const toastRect = await toast.boundingBox(); + expect(toastRect?.x).toBeGreaterThan(350); + }); + + test('bottom-start', async ({ mount }) => { + const component = await mount(App, { + props: { + toastProps: ['Hello world!', { position: 'bottom-start' }] + } + }); + + const toast = component.getByRole('status'); + // toast should be bottom left + const toastRect = await toast.boundingBox(); + expect(toastRect?.x).toBeLessThan(150); + }); + + test('bottom-end', async ({ mount }) => { + const component = await mount(App, { + props: { toastProps: ['Hello world!', { position: 'bottom-end' }] } + }); + + const toast = component.getByRole('status'); + // toast should be bottom right + const toastRect = await toast.boundingBox(); + expect(toastRect?.x).toBeGreaterThan(350); + }); +}); + +test.describe('RTL', () => { + test.beforeEach(async ({ page }) => { + await page.evaluate(() => { + document.documentElement.lang = 'ar'; + document.documentElement.dir = 'rtl'; + }); + }); + + test('top-start', async ({ mount }) => { + const component = await mount(App, { + props: { toastProps: ['مرحبا', { position: 'top-start' }] } + }); + + const toast = component.getByRole('status'); + // toast should be top right + const toastRect = await toast.boundingBox(); + expect(toastRect?.x).toBeGreaterThan(350); + }); + + test('top-end', async ({ mount }) => { + const component = await mount(App, { + props: { toastProps: ['مرحبا', { position: 'top-end' }] } + }); + + const toast = component.getByRole('status'); + // toast should be top left + const toastRect = await toast.boundingBox(); + expect(toastRect?.x).toBeLessThan(150); + }); + + test('bottom-start', async ({ mount }) => { + const component = await mount(App, { + props: { toastProps: ['مرحبا', { position: 'bottom-start' }] } + }); + + const toast = component.getByRole('status'); + // toast should be bottom right + const toastRect = await toast.boundingBox(); + expect(toastRect?.x).toBeGreaterThan(350); + }); + + test('bottom-end', async ({ mount }) => { + const component = await mount(App, { + props: { toastProps: ['مرحبا', { position: 'bottom-end' }] } + }); + + const toast = component.getByRole('status'); + // toast should be bottom left + const toastRect = await toast.boundingBox(); + expect(toastRect?.x).toBeLessThan(150); + }); +}); diff --git a/tests/components/toast.spec.ts b/tests/components/toast.spec.ts new file mode 100644 index 0000000..36c3c85 --- /dev/null +++ b/tests/components/toast.spec.ts @@ -0,0 +1,11 @@ +import { expect, test } from '@playwright/experimental-ct-svelte'; +import App from './boilerplate/App.svelte'; + +test.use({ viewport: { width: 500, height: 500 } }); + +test('should render', async ({ mount }) => { + const component = await mount(App, { + props: { toastProps: ['Hello world!'] } + }); + await expect(component.getByText('Hello world!')).toBeVisible(); +});