Skip to content
Closed
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
3 changes: 2 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ jobs:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- run: bun install --frozen-lockfile
- run: bun test
- run: bunx playwright install chromium --with-deps
- run: bun run test
- run: bun run build
14 changes: 10 additions & 4 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,16 @@ Default to using Bun instead of Node.js.

## Testing

Use `bun test` to run tests.

```ts#index.test.ts
import { test, expect } from "bun:test";
**Exception to the Bun default:** this repo tests with vitest browser mode
(`bun run test` → `vitest run`), executing the suite in real Chromium via
Playwright. Reason: selector semantics are this library's core domain, and
DOM emulators (happy-dom) diverge from spec exactly there — `querySelector`
subtree restriction hid a real scoping bug. Do not migrate tests back to
`bun test`, and import from `"vitest"` (with `vi.spyOn` for spies), not
`"bun:test"`.

```ts#example.test.ts
import { test, expect } from "vitest";

test("hello world", () => {
expect(1).toBe(1);
Expand Down
164 changes: 160 additions & 4 deletions bun.lock

Large diffs are not rendered by default.

2 changes: 0 additions & 2 deletions bunfig.toml

This file was deleted.

8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"changeset:version": "changeset version",
"prepublishOnly": "bun run build",
"release": "bun run build && changeset publish",
"test": "bun test"
"test": "vitest run"
},
"keywords": ["stimulus", "hotwired", "elements", "blessing", "dom"],
"license": "MIT",
Expand All @@ -33,9 +33,11 @@
"devDependencies": {
"@changesets/changelog-github": "^0.7.0",
"@changesets/cli": "^2.31.1",
"@happy-dom/global-registrator": "^20.10.6",
"@hotwired/stimulus": "^3.2",
"@types/bun": "latest",
"typescript": "^5"
"@vitest/browser-playwright": "^4.1.10",
"playwright": "^1.62.1",
"typescript": "^5",
"vitest": "^4.1.10"
}
Comment thread
myabc marked this conversation as resolved.
}
2 changes: 1 addition & 1 deletion test/barrel.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { test, expect } from "bun:test"
import { test, expect } from "vitest"
import { installElements, ElementsBlessing } from "../index"

test("barrel re-exports the public API", () => {
Expand Down
4 changes: 2 additions & 2 deletions test/blessing.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { test, expect, beforeEach, spyOn } from "bun:test"
import { test, expect, beforeEach, vi } from "vitest"
import { ElementsBlessing } from "../src/blessing"
import { resetSelectorWarnings } from "../src/query"

Expand Down Expand Up @@ -143,7 +143,7 @@ test("override applies to plural and has accessors", () => {
})

test("invalid override selector warns once and falls back to null / []", () => {
const warn = spyOn(console, "warn").mockImplementation(() => {})
const warn = vi.spyOn(console, "warn").mockImplementation(() => {})
class C {
static elements = { backdrop: "#backdrop" }
}
Expand Down
16 changes: 14 additions & 2 deletions test/harness.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
import { test, expect } from "bun:test"
import { test, expect } from "vitest"

test("happy-dom is registered", () => {
test("DOM is available", () => {
document.body.innerHTML = `<div id="x" class="a"></div>`
expect(document.querySelector("#x")).not.toBeNull()
expect(document.querySelectorAll(".a").length).toBe(1)
})

// Guards the reason this suite runs in a real browser at all: per spec,
// querySelector matches selectors document-wide and only filters results to
// descendants, so a combinator can reference ancestors OUTSIDE the query
// root. happy-dom restricted matching to the subtree (non-spec), which hid
// exactly this class of scoping behaviour from the suite. If this fails,
// the environment cannot faithfully exercise selector semantics.
test("environment is spec-correct: combinators match through outside ancestors", () => {
document.body.innerHTML = `<div class="wrap"><section id="root"><span class="item"></span></section></div>`
const root = document.getElementById("root")!
expect(root.querySelector(".wrap .item")).not.toBeNull()
})
2 changes: 1 addition & 1 deletion test/helpers.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { test, expect } from "bun:test"
import { test, expect } from "vitest"
import { camelize, capitalize, dasherize, readInheritableStaticObjectPairs } from "../src/helpers"

test("camelize handles snake_case, kebab-case, and passthrough", () => {
Expand Down
4 changes: 2 additions & 2 deletions test/integration.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { test, expect, beforeEach, afterEach, spyOn } from "bun:test"
import { test, expect, beforeEach, afterEach, vi } from "vitest"
import { Application, Controller } from "@hotwired/stimulus"
import { installElements } from "../src/install"
import { resetSelectorWarnings } from "../src/query"
Expand Down Expand Up @@ -76,7 +76,7 @@ test("selectors are scoped per controller — no cross-controller leakage", asyn
})

test("invalid selector on a live controller warns once and does not throw", async () => {
const warn = spyOn(console, "warn").mockImplementation(() => {})
const warn = vi.spyOn(console, "warn").mockImplementation(() => {})
class BadController extends Controller {
static elements = { oops: "###" }
}
Expand Down
8 changes: 4 additions & 4 deletions test/query.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { test, expect, beforeEach, spyOn } from "bun:test"
import { test, expect, beforeEach, vi } from "vitest"
import { queryOne, queryAll, resetSelectorWarnings } from "../src/query"

beforeEach(() => {
Expand Down Expand Up @@ -41,23 +41,23 @@ test("queryAll returns [] when nothing matches", () => {
})

test("missing root returns null / [] without warning", () => {
const warn = spyOn(console, "warn")
const warn = vi.spyOn(console, "warn")
expect(queryOne(null, ".item")).toBeNull()
expect(queryAll(undefined, ".item")).toEqual([])
expect(warn).not.toHaveBeenCalled()
warn.mockRestore()
})

test("invalid selector warns once and returns null / []", () => {
const warn = spyOn(console, "warn").mockImplementation(() => {})
const warn = vi.spyOn(console, "warn").mockImplementation(() => {})
expect(queryOne(root(), "###")).toBeNull()
expect(queryAll(root(), "###")).toEqual([])
expect(warn).toHaveBeenCalledTimes(1) // once per selector, across both calls
warn.mockRestore()
})

test("empty / whitespace selector warns once and returns null / []", () => {
const warn = spyOn(console, "warn").mockImplementation(() => {})
const warn = vi.spyOn(console, "warn").mockImplementation(() => {})
expect(queryOne(root(), " ")).toBeNull()
expect(queryAll(root(), " ")).toEqual([])
expect(warn).toHaveBeenCalledTimes(1)
Expand Down
3 changes: 0 additions & 3 deletions test/setup.ts

This file was deleted.

14 changes: 14 additions & 0 deletions vitest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { defineConfig } from "vitest/config"
import { playwright } from "@vitest/browser-playwright"

export default defineConfig({
test: {
browser: {
enabled: true,
provider: playwright(),
headless: true,
screenshotFailures: false,
instances: [{ browser: "chromium" }],
},
},
})
Loading