Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
19 changes: 19 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,22 @@ jobs:
steps:
- uses: wyvox/action@v1
- run: pnpm lint

e2e:
name: "E2E"
runs-on: ubuntu-latest
timeout-minutes: 15

steps:
- uses: wyvox/action@v1
- name: Install Playwright browser
run: pnpm --filter docs-for-ember-astro exec playwright install --with-deps chromium
- name: Run e2e tests
run: pnpm --filter docs-for-ember-astro test:e2e
- name: Upload report
if: ${{ !cancelled() }}
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: docs/playwright-report/
retention-days: 7
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
.env
.claude/
.env.production
.astro/
.log/
.DS_Store
node_modules/
dist/
test-results/
playwright-report/
playwright/.cache/
55 changes: 55 additions & 0 deletions docs/e2e/client-rendering.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { test, expect } from "@playwright/test";

/**
* These tests guard the core promise of ember-astro: that an Ember component
* placed in an Astro page with `client:only="ember"` actually mounts and
* renders in the browser. Each assertion targets output that ONLY the Ember
* component produces (not the surrounding static Astro markup), so if client
* rendering breaks, the island stays empty and the test fails.
*/

test("props: component renders with passed-in args", async ({ page }) => {
await page.goto("/examples/props");

// <Static greeting="Hello" who="World" /> renders `{{@props.greeting}}, {{@props.who}}`
await expect(page.getByText("Hello, World")).toBeVisible();
});

test("default block content: default slot renders as raw HTML", async ({ page }) => {
await page.goto("/examples/default-block-content");

// Demo renders `{{{@slots.default}}}` — the block content incl. a <strong>.
const island = page.locator("astro-island[component-export='Demo']");
await expect(island).toContainText("block content here");
await expect(island.locator("strong", { hasText: "here" })).toBeVisible();
});

test("named slots: each named slot renders into its fieldset", async ({ page }) => {
await page.goto("/examples/named-slots");

// The .container element only exists once the Ember component has rendered.
const container = page.locator(".container");
await expect(container).toBeVisible();

// Legends come from the .gjs template; slot bodies come from Astro slots
// (Astro strips the `slot="…"` attribute, so we match the rendered text).
for (const legend of ["@slots.one", "@slots.two", "@slots.three"]) {
await expect(container.locator("legend", { hasText: legend })).toBeVisible();
}
for (const body of ["one", "two", "three"]) {
await expect(container.getByText(body, { exact: true })).toBeVisible();
}
});

test("nested components: Outer renders and contains Inner", async ({ page }) => {
await page.goto("/examples/nested-components");

// Each component template renders its own fieldset+legend. Scope to the
// fieldset whose *direct* legend is "Outer" so we don't also match the
// static Astro wrapper <fieldset><legend>Demo</legend>.
const outer = page.locator('fieldset:has(> legend:text-is("Outer"))');
await expect(outer).toBeVisible();

// The Inner island must have rendered nested inside Outer.
await expect(outer.locator('legend:text-is("Inner")')).toBeVisible();
});
6 changes: 5 additions & 1 deletion docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,17 @@
"start": "astro dev",
"build": "astro build",
"preview": "astro preview",
"astro": "astro"
"astro": "astro",
"test:e2e": "playwright test"
},
"dependencies": {
"astro": "^7.0.3",
"ember-astro": "workspace:*",
"ember-source": "~7.1.0",
"sharp": "^0.34.2",
"vite": "^8.1.0"
},
"devDependencies": {
"@playwright/test": "^1.61.1"
}
}
32 changes: 32 additions & 0 deletions docs/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { defineConfig, devices } from "@playwright/test";

/**
* E2E tests run against a production build served by `astro preview`.
* This is the closest representation of what ships, and it exercises the
* ember-astro integration's build pipeline (babel + @embroider/vite) — so a
* regression that breaks client rendering of Ember components fails here.
*/
const PORT = 4321;

export default defineConfig({
testDir: "./e2e",
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
reporter: process.env.CI
? [["github"], ["html", { open: "never" }]]
: "list",
use: {
baseURL: `http://localhost:${PORT}`,
trace: "on-first-retry",
},
projects: [
{ name: "chromium", use: { ...devices["Desktop Chrome"] } },
],
webServer: {
command: `pnpm build && pnpm preview --port ${PORT}`,
url: `http://localhost:${PORT}`,
reuseExistingServer: !process.env.CI,
timeout: 120_000,
},
});
39 changes: 39 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading