├── .tool-versions ├── apps └── qwik-testing-library-e2e-tests │ ├── src │ ├── index.ts │ ├── root.tsx │ ├── components │ │ ├── qwik-core │ │ │ ├── qwik-signal.tsx │ │ │ ├── qwik-serialize.spec.tsx │ │ │ ├── qwik-computed.tsx │ │ │ ├── qwik-task.tsx │ │ │ ├── qwik-slot.tsx │ │ │ ├── qwik-store.tsx │ │ │ ├── qwik-visible-task.tsx │ │ │ ├── qwik-resource.tsx │ │ │ ├── qwik-context.tsx │ │ │ ├── qwik-serialize.tsx │ │ │ ├── qwik-store.spec.tsx │ │ │ ├── qwik-task.spec.tsx │ │ │ ├── qwik-signal.spec.tsx │ │ │ ├── qwik-context.spec.tsx │ │ │ ├── qwik-computed.spec.tsx │ │ │ ├── qwik-visible-task.spec.tsx │ │ │ ├── qwik-resource.spec.tsx │ │ │ ├── qwik-render.tsx │ │ │ ├── qwik-render.spec.tsx │ │ │ └── qwik-slot.spec.tsx │ │ ├── wrapper │ │ │ └── wrapper.spec.tsx │ │ ├── counter.tsx │ │ └── counter.spec.tsx │ ├── entry.dev.tsx │ └── entry.ssr.tsx │ ├── .prettierignore │ ├── .eslintignore │ ├── .gitignore │ ├── vitest.setup.ts │ ├── tsconfig.json │ ├── .eslintrc.cjs │ ├── vite.config.ts │ ├── README.md │ └── package.json ├── packages ├── qwik-mock │ ├── src │ │ ├── index.ts │ │ └── lib │ │ │ └── qwik-mock.ts │ ├── .prettierignore │ ├── README.md │ ├── .eslintignore │ ├── .gitignore │ ├── tsconfig.json │ ├── vite.config.ts │ ├── .eslintrc.cjs │ └── package.json └── qwik-testing-library │ ├── src │ ├── index.ts │ └── lib │ │ ├── types.ts │ │ └── qwik-testing-library.tsx │ ├── .prettierignore │ ├── .eslintignore │ ├── .gitignore │ ├── tsconfig.json │ ├── vite.config.ts │ ├── .eslintrc.cjs │ └── package.json ├── pnpm-workspace.yaml ├── high-voltage.png ├── .husky ├── pre-commit └── prepare-commit-msg ├── .github ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── feature_suggestion.yml │ ├── docs_suggestion.yml │ └── bug.yml ├── workflows │ ├── lint-pr.yml │ └── ci.yml ├── PULL_REQUEST_TEMPLATE.md └── dependabot.yml ├── .gitignore ├── scripts └── preview-release ├── LICENSE ├── release.config.mjs ├── .all-contributorsrc ├── package.json ├── CONTRIBUTING.md ├── CODE_OF_CONDUCT.md └── README.md /.tool-versions: -------------------------------------------------------------------------------- 1 | pnpm 9.11.0 2 | nodejs 22.9.0 3 | -------------------------------------------------------------------------------- /apps/qwik-testing-library-e2e-tests/src/index.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/qwik-mock/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./lib/qwik-mock"; 2 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - 'apps/*' 3 | - 'packages/*' -------------------------------------------------------------------------------- /packages/qwik-testing-library/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./lib/qwik-testing-library"; 2 | -------------------------------------------------------------------------------- /high-voltage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ianlet/qwik-testing-library/HEAD/high-voltage.png -------------------------------------------------------------------------------- /packages/qwik-mock/.prettierignore: -------------------------------------------------------------------------------- 1 | # Files Prettier should not format 2 | **/*.log 3 | **/.DS_Store 4 | *. 5 | dist 6 | node_modules 7 | -------------------------------------------------------------------------------- /packages/qwik-testing-library/.prettierignore: -------------------------------------------------------------------------------- 1 | # Files Prettier should not format 2 | **/*.log 3 | **/.DS_Store 4 | *. 5 | dist 6 | node_modules 7 | -------------------------------------------------------------------------------- /apps/qwik-testing-library-e2e-tests/.prettierignore: -------------------------------------------------------------------------------- 1 | # Files Prettier should not format 2 | **/*.log 3 | **/.DS_Store 4 | *. 5 | dist 6 | node_modules 7 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | BRANCH_NAME=$(git branch | grep '*' | sed 's/* //') 2 | if [[ $BRANCH_NAME =~ "no branch" ]]; then 3 | echo "You are rebasing, skipping hook" 4 | exit 0 5 | fi 6 | 7 | pnpm validate 8 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | contact_links: 2 | - name: 🤔 Support & Questions 3 | url: https://qwik.dev/chat 4 | about: This issue tracker is not for support questions. Please post your question on the Qwik Discord. -------------------------------------------------------------------------------- /.husky/prepare-commit-msg: -------------------------------------------------------------------------------- 1 | BRANCH_NAME=$(git branch | grep '*' | sed 's/* //') 2 | if [[ $BRANCH_NAME =~ "no branch" ]]; then 3 | echo "You are rebasing, skipping hook" 4 | exit 0 5 | fi 6 | 7 | exec < /dev/tty && pnpm exec cz --hook || true 8 | -------------------------------------------------------------------------------- /apps/qwik-testing-library-e2e-tests/src/root.tsx: -------------------------------------------------------------------------------- 1 | export default () => { 2 | return ( 3 | <> 4 | 5 | 6 | Qwik Blank App 7 | 8 | 9 | 10 | ); 11 | }; 12 | -------------------------------------------------------------------------------- /packages/qwik-mock/README.md: -------------------------------------------------------------------------------- 1 | # Qwik Mock 2 | 3 | Qwik Mock is a small utility to mock Qwik QRLs. It is currently experimental and should be used with caution. 4 | 5 | Head over to [qtl-docs-repo] to learn how to use it. 6 | 7 | [qtl-docs-repo]: https://github.com/ianlet/qwik-testing-library/blob/main/README.md 8 | 9 | -------------------------------------------------------------------------------- /.github/workflows/lint-pr.yml: -------------------------------------------------------------------------------- 1 | name: 'Lint PR' 2 | 3 | on: 4 | pull_request_target: 5 | types: 6 | - opened 7 | - edited 8 | - synchronize 9 | 10 | jobs: 11 | main: 12 | name: Validate PR title 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: amannn/action-semantic-pull-request@v6 16 | env: 17 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /apps/qwik-testing-library-e2e-tests/src/components/qwik-core/qwik-signal.tsx: -------------------------------------------------------------------------------- 1 | import { component$, useSignal } from "@builder.io/qwik"; 2 | 3 | export const QwikSignal = component$(() => { 4 | const sig = useSignal("my-signal"); 5 | 6 | return ( 7 |
8 | {sig.value} 9 | 10 |
11 | ); 12 | }); 13 | -------------------------------------------------------------------------------- /apps/qwik-testing-library-e2e-tests/src/components/qwik-core/qwik-serialize.spec.tsx: -------------------------------------------------------------------------------- 1 | import { render, screen } from "@noma.to/qwik-testing-library"; 2 | import { QwikSerialize } from "./qwik-serialize"; 3 | 4 | describe("", () => { 5 | it("should render non-serializable value", async () => { 6 | await render(); 7 | 8 | expect(screen.getByText("no-serialize-foo")).toBeInTheDocument(); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_suggestion.yml: -------------------------------------------------------------------------------- 1 | name: ✨ Suggestion or Feature Request 2 | description: Suggestions on how we can improve the library. 3 | title: '[✨]' 4 | labels: [ 'enhancement', 'STATUS-1: needs triage' ] 5 | body: 6 | - type: textarea 7 | id: description 8 | attributes: 9 | description: 'A clear and concise description of your suggestion or feature request' 10 | label: Suggestion 11 | validations: 12 | required: true -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/docs_suggestion.yml: -------------------------------------------------------------------------------- 1 | name: 📖 Documentation Suggestion 2 | description: Suggestions on how we can improve the documentation. 3 | title: '[📖]' 4 | labels: [ 'documentation', 'STATUS-1: needs triage' ] 5 | body: 6 | - type: textarea 7 | id: description 8 | attributes: 9 | description: 'A clear and concise description of your suggestion to improve the docs.' 10 | label: Suggestion 11 | validations: 12 | required: true -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Development 2 | node_modules 3 | 4 | # Cache 5 | .cache 6 | .mf 7 | .vscode 8 | .rollup.cache 9 | tsconfig.tsbuildinfo 10 | 11 | # Logs 12 | logs 13 | *.log 14 | npm-debug.log* 15 | yarn-debug.log* 16 | yarn-error.log* 17 | pnpm-debug.log* 18 | lerna-debug.log* 19 | 20 | # Editor 21 | !.vscode/extensions.json 22 | .idea 23 | .DS_Store 24 | *.suo 25 | *.ntvs* 26 | *.njsproj 27 | *.sln 28 | *.sw? 29 | 30 | # Yarn 31 | .yarn/* 32 | !.yarn/releases 33 | -------------------------------------------------------------------------------- /packages/qwik-mock/.eslintignore: -------------------------------------------------------------------------------- 1 | **/*.log 2 | **/.DS_Store 3 | *. 4 | .vscode/settings.json 5 | .history 6 | .yarn 7 | bazel-* 8 | bazel-bin 9 | bazel-out 10 | bazel-qwik 11 | bazel-testlogs 12 | dist 13 | dist-dev 14 | lib 15 | lib-types 16 | etc 17 | external 18 | node_modules 19 | temp 20 | tsc-out 21 | tsdoc-metadata.json 22 | target 23 | output 24 | rollup.config.js 25 | build 26 | .cache 27 | .vscode 28 | .rollup.cache 29 | dist 30 | tsconfig.tsbuildinfo 31 | vite.config.ts 32 | -------------------------------------------------------------------------------- /packages/qwik-testing-library/.eslintignore: -------------------------------------------------------------------------------- 1 | **/*.log 2 | **/.DS_Store 3 | *. 4 | .vscode/settings.json 5 | .history 6 | .yarn 7 | bazel-* 8 | bazel-bin 9 | bazel-out 10 | bazel-qwik 11 | bazel-testlogs 12 | dist 13 | dist-dev 14 | lib 15 | lib-types 16 | etc 17 | external 18 | node_modules 19 | temp 20 | tsc-out 21 | tsdoc-metadata.json 22 | target 23 | output 24 | rollup.config.js 25 | build 26 | .cache 27 | .vscode 28 | .rollup.cache 29 | dist 30 | tsconfig.tsbuildinfo 31 | vite.config.ts 32 | -------------------------------------------------------------------------------- /apps/qwik-testing-library-e2e-tests/.eslintignore: -------------------------------------------------------------------------------- 1 | **/*.log 2 | **/.DS_Store 3 | *. 4 | .vscode/settings.json 5 | .history 6 | .yarn 7 | bazel-* 8 | bazel-bin 9 | bazel-out 10 | bazel-qwik 11 | bazel-testlogs 12 | dist 13 | dist-dev 14 | lib 15 | lib-types 16 | etc 17 | external 18 | node_modules 19 | temp 20 | tsc-out 21 | tsdoc-metadata.json 22 | target 23 | output 24 | rollup.config.js 25 | build 26 | .cache 27 | .vscode 28 | .rollup.cache 29 | dist 30 | tsconfig.tsbuildinfo 31 | vite.config.ts 32 | -------------------------------------------------------------------------------- /apps/qwik-testing-library-e2e-tests/src/components/qwik-core/qwik-computed.tsx: -------------------------------------------------------------------------------- 1 | import { component$, useComputed$, useSignal } from "@builder.io/qwik"; 2 | 3 | export const QwikComputed = component$(() => { 4 | const sig = useSignal(0); 5 | 6 | const computed = useComputed$(() => { 7 | return "computed-value-" + sig.value; 8 | }); 9 | 10 | return ( 11 |
12 | {computed.value} 13 | 14 |
15 | ); 16 | }); 17 | -------------------------------------------------------------------------------- /apps/qwik-testing-library-e2e-tests/src/components/qwik-core/qwik-task.tsx: -------------------------------------------------------------------------------- 1 | import { component$, useSignal, useTask$ } from "@builder.io/qwik"; 2 | 3 | export const QwikTask = component$(() => { 4 | const sig = useSignal(0); 5 | const foo = useSignal(); 6 | 7 | useTask$(({ track }) => { 8 | const value = track(sig); 9 | 10 | foo.value = "foo-" + value; 11 | }); 12 | 13 | return ( 14 |
15 | {foo.value} 16 | 17 |
18 | ); 19 | }); 20 | -------------------------------------------------------------------------------- /packages/qwik-mock/.gitignore: -------------------------------------------------------------------------------- 1 | # Build 2 | /dist 3 | /lib 4 | /lib-types 5 | /server 6 | 7 | # Development 8 | node_modules 9 | 10 | # Cache 11 | .cache 12 | .mf 13 | .vscode 14 | .rollup.cache 15 | tsconfig.tsbuildinfo 16 | 17 | # Logs 18 | logs 19 | *.log 20 | npm-debug.log* 21 | yarn-debug.log* 22 | yarn-error.log* 23 | pnpm-debug.log* 24 | lerna-debug.log* 25 | 26 | # Editor 27 | !.vscode/extensions.json 28 | .idea 29 | .DS_Store 30 | *.suo 31 | *.ntvs* 32 | *.njsproj 33 | *.sln 34 | *.sw? 35 | 36 | # Yarn 37 | .yarn/* 38 | !.yarn/releases 39 | -------------------------------------------------------------------------------- /packages/qwik-testing-library/.gitignore: -------------------------------------------------------------------------------- 1 | # Build 2 | /dist 3 | /lib 4 | /lib-types 5 | /server 6 | 7 | # Development 8 | node_modules 9 | 10 | # Cache 11 | .cache 12 | .mf 13 | .vscode 14 | .rollup.cache 15 | tsconfig.tsbuildinfo 16 | 17 | # Logs 18 | logs 19 | *.log 20 | npm-debug.log* 21 | yarn-debug.log* 22 | yarn-error.log* 23 | pnpm-debug.log* 24 | lerna-debug.log* 25 | 26 | # Editor 27 | !.vscode/extensions.json 28 | .idea 29 | .DS_Store 30 | *.suo 31 | *.ntvs* 32 | *.njsproj 33 | *.sln 34 | *.sw? 35 | 36 | # Yarn 37 | .yarn/* 38 | !.yarn/releases 39 | -------------------------------------------------------------------------------- /apps/qwik-testing-library-e2e-tests/.gitignore: -------------------------------------------------------------------------------- 1 | # Build 2 | /dist 3 | /lib 4 | /lib-types 5 | /server 6 | 7 | # Development 8 | node_modules 9 | 10 | # Cache 11 | .cache 12 | .mf 13 | .vscode 14 | .rollup.cache 15 | tsconfig.tsbuildinfo 16 | 17 | # Logs 18 | logs 19 | *.log 20 | npm-debug.log* 21 | yarn-debug.log* 22 | yarn-error.log* 23 | pnpm-debug.log* 24 | lerna-debug.log* 25 | 26 | # Editor 27 | !.vscode/extensions.json 28 | .idea 29 | .DS_Store 30 | *.suo 31 | *.ntvs* 32 | *.njsproj 33 | *.sln 34 | *.sw? 35 | 36 | # Yarn 37 | .yarn/* 38 | !.yarn/releases 39 | -------------------------------------------------------------------------------- /apps/qwik-testing-library-e2e-tests/src/components/qwik-core/qwik-slot.tsx: -------------------------------------------------------------------------------- 1 | import { component$, Slot, useSignal } from "@builder.io/qwik"; 2 | 3 | export const QwikSlot = component$(() => { 4 | const active = useSignal(false); 5 | 6 | return ( 7 |
8 |
9 | 10 |
11 |
12 | 13 |
14 |
{active.value && }
15 | 16 |
17 | ); 18 | }); 19 | -------------------------------------------------------------------------------- /scripts/preview-release: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Preview the next release from a branch 3 | # 4 | # Prerequisites: 5 | # - You must have push access to repository at the `origin` URL 6 | # - The branch you are on must exist on `origin` 7 | 8 | set -euxo pipefail 9 | 10 | branch="$(git rev-parse --abbrev-ref HEAD)" 11 | repository_url="$(git remote get-url origin)" 12 | 13 | pnpm semantic-release \ 14 | --plugins="@semantic-release/commit-analyzer,@semantic-release/release-notes-generator" \ 15 | --dry-run \ 16 | --branches="$branch" \ 17 | --repository-url="$repository_url" -------------------------------------------------------------------------------- /apps/qwik-testing-library-e2e-tests/src/components/qwik-core/qwik-store.tsx: -------------------------------------------------------------------------------- 1 | import { $, component$, useStore } from "@builder.io/qwik"; 2 | 3 | export const QwikStore = component$(() => { 4 | const store = useStore<{ foo: string; bar: string }>({ 5 | foo: "foo", 6 | bar: "bar", 7 | }); 8 | 9 | return ( 10 |
11 | {store.foo} - {store.bar} 12 | 20 |
21 | ); 22 | }); 23 | -------------------------------------------------------------------------------- /packages/qwik-mock/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "target": "ES2017", 5 | "module": "ES2020", 6 | "lib": ["es2020", "DOM"], 7 | "jsx": "react-jsx", 8 | "jsxImportSource": "@builder.io/qwik", 9 | "strict": true, 10 | "declaration": true, 11 | "declarationDir": "lib-types", 12 | "resolveJsonModule": true, 13 | "moduleResolution": "Bundler", 14 | "esModuleInterop": true, 15 | "skipLibCheck": true, 16 | "incremental": true, 17 | "isolatedModules": true, 18 | "types": ["vite/client"] 19 | }, 20 | "include": ["src"] 21 | } 22 | -------------------------------------------------------------------------------- /apps/qwik-testing-library-e2e-tests/src/components/qwik-core/qwik-visible-task.tsx: -------------------------------------------------------------------------------- 1 | import { component$, useSignal, useVisibleTask$ } from "@builder.io/qwik"; 2 | 3 | export const QwikVisibleTask = component$(() => { 4 | const sig = useSignal(0); 5 | const foo = useSignal(); 6 | 7 | // eslint-disable-next-line qwik/no-use-visible-task 8 | useVisibleTask$(({ track }) => { 9 | const value = track(sig); 10 | 11 | foo.value = "foo-" + value; 12 | }); 13 | 14 | return ( 15 |
16 | {foo.value} 17 | 18 |
19 | ); 20 | }); 21 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # What is it? 2 | 3 | - [ ] Feature / enhancement 4 | - [ ] Bug 5 | - [ ] Docs / tests / types / typos 6 | - [ ] Infra 7 | 8 | # Description 9 | 10 | 14 | 15 | # Checklist: 16 | 17 | - [ ] My code follows 18 | the [developer guidelines of this project](https://github.com/ianlet/qwik-testing-library/blob/main/CONTRIBUTING.md) 19 | - [ ] I have performed a self-review of my own code 20 | - [ ] I have made corresponding changes to the docs 21 | - [ ] Added new tests to cover the fix / functionality -------------------------------------------------------------------------------- /apps/qwik-testing-library-e2e-tests/src/components/wrapper/wrapper.spec.tsx: -------------------------------------------------------------------------------- 1 | import { render, screen } from "@noma.to/qwik-testing-library"; 2 | import { component$, Slot } from "@builder.io/qwik"; 3 | 4 | const MyComponent = component$(() =>
my-component
); 5 | 6 | const Wrapper = component$(() => { 7 | return ( 8 |
9 | 10 |
11 | ); 12 | }); 13 | 14 | describe("Wrapper", () => { 15 | it("should wrap my component inside the wrapper", async () => { 16 | await render(, { wrapper: Wrapper }); 17 | 18 | expect(screen.getByTestId("wrapper")).toHaveTextContent("my-component"); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /apps/qwik-testing-library-e2e-tests/vitest.setup.ts: -------------------------------------------------------------------------------- 1 | import "@testing-library/jest-dom/vitest"; 2 | import { beforeEach, vi } from "vitest"; 3 | 4 | // This has to run before qdev.ts loads. `beforeAll` is too late 5 | globalThis.qTest = false; // Forces Qwik to run as if it was in a Browser 6 | globalThis.qRuntimeQrl = true; 7 | globalThis.qDev = true; 8 | globalThis.qInspector = false; 9 | 10 | beforeEach(() => { 11 | const mockIntersectionObserver = vi.fn(); 12 | mockIntersectionObserver.mockReturnValue({ 13 | observe: () => null, 14 | unobserve: () => null, 15 | disconnect: () => null, 16 | }); 17 | window.IntersectionObserver = mockIntersectionObserver; 18 | }); 19 | -------------------------------------------------------------------------------- /apps/qwik-testing-library-e2e-tests/src/entry.dev.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * WHAT IS THIS FILE? 3 | * 4 | * Development entry point using only client-side modules: 5 | * - Do not use this mode in production! 6 | * - No SSR 7 | * - No portion of the application is pre-rendered on the server. 8 | * - All of the application is running eagerly in the browser. 9 | * - More code is transferred to the browser than in SSR mode. 10 | * - Optimizer/Serialization/Deserialization code is not exercised! 11 | */ 12 | import { render, type RenderOptions } from "@builder.io/qwik"; 13 | import Root from "./root"; 14 | 15 | export default function (opts: RenderOptions) { 16 | return render(document, , opts); 17 | } 18 | -------------------------------------------------------------------------------- /apps/qwik-testing-library-e2e-tests/src/entry.ssr.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * WHAT IS THIS FILE? 3 | * 4 | * SSR entry point, in all cases the application is rendered outside the browser, this 5 | * entry point will be the common one. 6 | * 7 | * - Server (express, cloudflare...) 8 | * - npm run start 9 | * - npm run preview 10 | * - npm run build 11 | * 12 | */ 13 | import { 14 | renderToStream, 15 | type RenderToStreamOptions, 16 | } from "@builder.io/qwik/server"; 17 | import { manifest } from "@qwik-client-manifest"; 18 | import Root from "./root"; 19 | 20 | export default function (opts: RenderToStreamOptions) { 21 | return renderToStream(, { 22 | manifest, 23 | ...opts, 24 | }); 25 | } 26 | -------------------------------------------------------------------------------- /apps/qwik-testing-library-e2e-tests/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "target": "ES2017", 5 | "module": "ES2020", 6 | "lib": ["es2020", "DOM"], 7 | "jsx": "react-jsx", 8 | "jsxImportSource": "@builder.io/qwik", 9 | "strict": true, 10 | "declaration": true, 11 | "declarationDir": "lib-types", 12 | "resolveJsonModule": true, 13 | "moduleResolution": "Bundler", 14 | "esModuleInterop": true, 15 | "skipLibCheck": true, 16 | "incremental": true, 17 | "isolatedModules": true, 18 | "types": [ 19 | "vite/client", 20 | "vitest/globals", 21 | "@testing-library/jest-dom/vitest" 22 | ] 23 | }, 24 | "include": ["src"] 25 | } 26 | -------------------------------------------------------------------------------- /apps/qwik-testing-library-e2e-tests/src/components/qwik-core/qwik-resource.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | component$, 3 | Resource, 4 | useResource$, 5 | useSignal, 6 | } from "@builder.io/qwik"; 7 | 8 | function getResource(value: number): Promise { 9 | return Promise.resolve("resource-" + value); 10 | } 11 | 12 | export const QwikResource = component$(() => { 13 | const sig = useSignal(0); 14 | 15 | const resource = useResource$(({ track }) => { 16 | const value = track(sig); 17 | return getResource(value); 18 | }); 19 | 20 | return ( 21 |
22 | <>{value}} /> 23 | 24 |
25 | ); 26 | }); 27 | -------------------------------------------------------------------------------- /packages/qwik-testing-library/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "target": "ES2017", 5 | "module": "ES2020", 6 | "lib": [ 7 | "es2020", 8 | "DOM" 9 | ], 10 | "jsx": "react-jsx", 11 | "jsxImportSource": "@builder.io/qwik", 12 | "strict": true, 13 | "declaration": true, 14 | "declarationDir": "lib-types", 15 | "resolveJsonModule": true, 16 | "moduleResolution": "Bundler", 17 | "esModuleInterop": true, 18 | "skipLibCheck": true, 19 | "incremental": true, 20 | "isolatedModules": true, 21 | "types": [ 22 | "node", 23 | "vite/client", 24 | "vitest/globals" 25 | ] 26 | }, 27 | "include": [ 28 | "src" 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /apps/qwik-testing-library-e2e-tests/src/components/qwik-core/qwik-context.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | component$, 3 | createContextId, 4 | useContext, 5 | useContextProvider, 6 | useStore, 7 | } from "@builder.io/qwik"; 8 | 9 | const MyContext = createContextId<{ foo: string }>("my-context"); 10 | 11 | export const QwikContext = component$(() => { 12 | useContextProvider(MyContext, useStore({ foo: "foo" })); 13 | 14 | return ( 15 |
16 | 17 |
18 | ); 19 | }); 20 | 21 | const Inner = component$(() => { 22 | const context = useContext(MyContext); 23 | 24 | return ( 25 |
26 | context-{context.foo} 27 | 28 |
29 | ); 30 | }); 31 | -------------------------------------------------------------------------------- /apps/qwik-testing-library-e2e-tests/src/components/qwik-core/qwik-serialize.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | component$, 3 | noSerialize, 4 | NoSerialize, 5 | useSignal, 6 | useVisibleTask$, 7 | } from "@builder.io/qwik"; 8 | 9 | class MyClass { 10 | constructor(private _foo: string) {} 11 | 12 | get foo(): string { 13 | return this._foo; 14 | } 15 | 16 | set foo(value: string) { 17 | this._foo = value; 18 | } 19 | } 20 | 21 | export const QwikSerialize = component$(() => { 22 | const sig = useSignal>(); 23 | 24 | // eslint-disable-next-line qwik/no-use-visible-task 25 | useVisibleTask$(() => { 26 | sig.value = noSerialize(new MyClass("no-serialize-foo")); 27 | }); 28 | 29 | return
{sig.value?.foo}
; 30 | }); 31 | -------------------------------------------------------------------------------- /apps/qwik-testing-library-e2e-tests/src/components/counter.tsx: -------------------------------------------------------------------------------- 1 | import { $, component$, QRL, useSignal } from "@builder.io/qwik"; 2 | 3 | interface CounterProps { 4 | onChange$: QRL<(value: number) => void>; 5 | } 6 | 7 | export const Counter = component$(({ onChange$ }) => { 8 | const count = useSignal(0); 9 | 10 | const handleIncrement = $(() => { 11 | count.value++; 12 | return onChange$(count.value); 13 | }); 14 | 15 | const handleDecrement = $(() => { 16 | count.value--; 17 | return onChange$(count.value); 18 | }); 19 | 20 | return ( 21 |
22 |

Counter: {count}

23 | 24 | 25 |
26 | ); 27 | }); 28 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | # Update npm dependencies 4 | - package-ecosystem: "npm" 5 | directory: "/" 6 | schedule: 7 | interval: "monthly" 8 | groups: 9 | env: 10 | patterns: 11 | - "*jsdom*" 12 | - "*happy-dom*" 13 | all: 14 | dependency-type: "all" 15 | ignore: 16 | - dependency-name: "eslint" 17 | versions: [">=9"] 18 | - dependency-name: "eslint-plugin-n" 19 | versions: [">=17"] 20 | - dependency-name: "eslint-plugin-promise" 21 | versions: [">=7"] 22 | 23 | # Update GitHub Actions dependencies 24 | - package-ecosystem: "github-actions" 25 | directory: "/" 26 | schedule: 27 | interval: "monthly" 28 | groups: 29 | actions: 30 | patterns: 31 | - "*" 32 | -------------------------------------------------------------------------------- /apps/qwik-testing-library-e2e-tests/src/components/qwik-core/qwik-store.spec.tsx: -------------------------------------------------------------------------------- 1 | import { render, screen, waitFor } from "@noma.to/qwik-testing-library"; 2 | import { QwikStore } from "./qwik-store"; 3 | import { userEvent } from "@testing-library/user-event"; 4 | 5 | describe("", () => { 6 | it("should render", async () => { 7 | await render(); 8 | 9 | expect(screen.getByText("foo - bar")).toBeInTheDocument(); 10 | }); 11 | 12 | describe("on update", () => { 13 | it("should render updated store value", async () => { 14 | const user = userEvent.setup(); 15 | await render(); 16 | 17 | const button = screen.getByRole("button", { name: "update" }); 18 | await user.click(button); 19 | 20 | await waitFor(() => 21 | expect(screen.getByText("bar - baz")).toBeInTheDocument(), 22 | ); 23 | }); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /apps/qwik-testing-library-e2e-tests/src/components/qwik-core/qwik-task.spec.tsx: -------------------------------------------------------------------------------- 1 | import { render, screen, waitFor } from "@noma.to/qwik-testing-library"; 2 | import { QwikTask } from "./qwik-task"; 3 | import { userEvent } from "@testing-library/user-event"; 4 | 5 | describe("", () => { 6 | it("should render value from task", async () => { 7 | await render(); 8 | 9 | expect(screen.getByText("foo-0")).toBeInTheDocument(); 10 | }); 11 | 12 | describe("on update", () => { 13 | it("should render value updated from task", async () => { 14 | const user = userEvent.setup(); 15 | await render(); 16 | 17 | const button = screen.getByRole("button", { name: "update" }); 18 | await user.click(button); 19 | 20 | await waitFor(() => 21 | expect(screen.getByText("foo-1")).toBeInTheDocument(), 22 | ); 23 | }); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /apps/qwik-testing-library-e2e-tests/src/components/qwik-core/qwik-signal.spec.tsx: -------------------------------------------------------------------------------- 1 | import { render, screen, waitFor } from "@noma.to/qwik-testing-library"; 2 | import { QwikSignal } from "./qwik-signal"; 3 | import { userEvent } from "@testing-library/user-event"; 4 | 5 | describe("", () => { 6 | it("should render signal value", async () => { 7 | await render(); 8 | 9 | expect(screen.getByText("my-signal")).toBeInTheDocument(); 10 | }); 11 | 12 | describe("on update", () => { 13 | it("should render updated signal value", async () => { 14 | const user = userEvent.setup(); 15 | await render(); 16 | 17 | const button = screen.getByRole("button", { name: "update" }); 18 | await user.click(button); 19 | 20 | await waitFor(() => 21 | expect(screen.getByText("updated-signal")).toBeInTheDocument(), 22 | ); 23 | }); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /apps/qwik-testing-library-e2e-tests/src/components/qwik-core/qwik-context.spec.tsx: -------------------------------------------------------------------------------- 1 | import { render, screen, waitFor } from "@noma.to/qwik-testing-library"; 2 | import { userEvent } from "@testing-library/user-event"; 3 | import { QwikContext } from "./qwik-context"; 4 | 5 | describe("", () => { 6 | it("should render context value", async () => { 7 | await render(); 8 | 9 | expect(screen.getByText("context-foo")).toBeInTheDocument(); 10 | }); 11 | 12 | describe("on update", () => { 13 | it("should render updated context value", async () => { 14 | const user = userEvent.setup(); 15 | await render(); 16 | 17 | const button = screen.getByRole("button", { name: "update" }); 18 | await user.click(button); 19 | 20 | await waitFor(() => 21 | expect(screen.getByText("context-bar")).toBeInTheDocument(), 22 | ); 23 | }); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /apps/qwik-testing-library-e2e-tests/src/components/qwik-core/qwik-computed.spec.tsx: -------------------------------------------------------------------------------- 1 | import { render, screen, waitFor } from "@noma.to/qwik-testing-library"; 2 | import { QwikComputed } from "./qwik-computed"; 3 | import { userEvent } from "@testing-library/user-event"; 4 | 5 | describe("", () => { 6 | it("should render computed value", async () => { 7 | await render(); 8 | 9 | expect(screen.getByText("computed-value-0")).toBeInTheDocument(); 10 | }); 11 | 12 | describe("on tracked value update", () => { 13 | it("should render updated computed value", async () => { 14 | const user = userEvent.setup(); 15 | await render(); 16 | 17 | const button = screen.getByRole("button", { name: "update" }); 18 | await user.click(button); 19 | 20 | await waitFor(() => 21 | expect(screen.getByText("computed-value-1")).toBeInTheDocument(), 22 | ); 23 | }); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /apps/qwik-testing-library-e2e-tests/src/components/qwik-core/qwik-visible-task.spec.tsx: -------------------------------------------------------------------------------- 1 | import { render, screen, waitFor } from "@noma.to/qwik-testing-library"; 2 | import { userEvent } from "@testing-library/user-event"; 3 | import { QwikVisibleTask } from "./qwik-visible-task"; 4 | 5 | describe("", () => { 6 | it("should render value from visible task", async () => { 7 | await render(); 8 | 9 | expect(screen.getByText("foo-0")).toBeInTheDocument(); 10 | }); 11 | 12 | describe("on update", () => { 13 | it("should render value updated from visible task", async () => { 14 | const user = userEvent.setup(); 15 | await render(); 16 | 17 | const button = screen.getByRole("button", { name: "update" }); 18 | await user.click(button); 19 | 20 | await waitFor(() => 21 | expect(screen.getByText("foo-1")).toBeInTheDocument(), 22 | ); 23 | }); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /packages/qwik-testing-library/src/lib/types.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | BoundFunctions, 3 | prettyFormat, 4 | Queries, 5 | } from "@testing-library/dom"; 6 | import { queries } from "@testing-library/dom"; 7 | import type { Component, RenderOptions } from "@builder.io/qwik"; 8 | 9 | export interface Options extends RenderOptions { 10 | container?: HTMLElement; 11 | baseElement?: HTMLElement; 12 | queries?: Queries & typeof queries; 13 | wrapper?: Component; 14 | } 15 | 16 | export type DebugFn = ( 17 | baseElement?: HTMLElement | HTMLElement[], 18 | maxLength?: number, 19 | options?: prettyFormat.OptionsReceived, 20 | ) => void; 21 | 22 | export type Result = BoundFunctions & { 23 | asFragment: () => DocumentFragment; 24 | container: HTMLElement; 25 | baseElement: HTMLElement; 26 | debug: DebugFn; 27 | unmount: () => void; 28 | }; 29 | 30 | export type ComponentRef = { 31 | container: HTMLElement; 32 | componentCleanup: () => void; 33 | }; 34 | -------------------------------------------------------------------------------- /apps/qwik-testing-library-e2e-tests/src/components/qwik-core/qwik-resource.spec.tsx: -------------------------------------------------------------------------------- 1 | import { render, screen, waitFor } from "@noma.to/qwik-testing-library"; 2 | import { QwikResource } from "./qwik-resource"; 3 | import { userEvent } from "@testing-library/user-event"; 4 | 5 | describe("", () => { 6 | it("should render the resource", async () => { 7 | await render(); 8 | 9 | await waitFor(() => 10 | expect(screen.getByText("resource-0")).toBeInTheDocument(), 11 | ); 12 | }); 13 | 14 | describe("on tracked value update", () => { 15 | it("should render updated resource value", async () => { 16 | const user = userEvent.setup(); 17 | await render(); 18 | 19 | const button = screen.getByRole("button", { name: "update" }); 20 | await user.click(button); 21 | 22 | await waitFor(() => 23 | expect(screen.getByText("resource-1")).toBeInTheDocument(), 24 | ); 25 | }); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /apps/qwik-testing-library-e2e-tests/src/components/qwik-core/qwik-render.tsx: -------------------------------------------------------------------------------- 1 | import { $, component$, PropsOf, Slot, useSignal } from "@builder.io/qwik"; 2 | 3 | interface QwikRenderProps extends PropsOf<"div"> { 4 | myProp?: string; 5 | items?: string[]; 6 | } 7 | 8 | export const QwikRender = component$( 9 | ({ myProp = "qwik-prop", items = [] }) => { 10 | const propSig = useSignal(myProp); 11 | 12 | const changePropValue = $(() => { 13 | propSig.value = "changed-prop"; 14 | }); 15 | 16 | return ( 17 |
18 | 19 | 20 |
    21 | {items.map((item) => ( 22 |
  • {item}
  • 23 | ))} 24 |
25 | 26 | 27 |
28 | ); 29 | }, 30 | ); 31 | 32 | const ChildComponent = component$(({ myProp }) => { 33 | return ( 34 | <> 35 |
{myProp}
36 |
37 | 38 |
39 |
40 | 41 |
42 | 43 | ); 44 | }); 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Ian Letourneau (https://github.com/ianlet) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /packages/qwik-mock/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import pkg from "./package.json"; 3 | import { qwikVite } from "@builder.io/qwik/optimizer"; 4 | import tsconfigPaths from "vite-tsconfig-paths"; 5 | 6 | const { dependencies = {}, peerDependencies = {} } = pkg as any; 7 | const makeRegex = (dep) => new RegExp(`^${dep}(/.*)?$`); 8 | const excludeAll = (obj) => Object.keys(obj).map(makeRegex); 9 | 10 | export default defineConfig(() => { 11 | return { 12 | build: { 13 | target: "es2020", 14 | lib: { 15 | entry: "./src/index.ts", 16 | formats: ["es", "cjs"], 17 | fileName: (format, entryName) => 18 | `${entryName}.qwik.${format === "es" ? "mjs" : "cjs"}`, 19 | }, 20 | rollupOptions: { 21 | output: { 22 | preserveModules: true, 23 | preserveModulesRoot: "src", 24 | }, 25 | // externalize deps that shouldn't be bundled into the library 26 | external: [ 27 | /^node:.*/, 28 | ...excludeAll(dependencies), 29 | ...excludeAll(peerDependencies), 30 | ], 31 | }, 32 | }, 33 | plugins: [qwikVite(), tsconfigPaths()], 34 | }; 35 | }); 36 | -------------------------------------------------------------------------------- /packages/qwik-testing-library/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import pkg from "./package.json"; 3 | import { qwikVite } from "@builder.io/qwik/optimizer"; 4 | import tsconfigPaths from "vite-tsconfig-paths"; 5 | 6 | const { dependencies = {}, peerDependencies = {} } = pkg as any; 7 | const makeRegex = (dep) => new RegExp(`^${dep}(/.*)?$`); 8 | const excludeAll = (obj) => Object.keys(obj).map(makeRegex); 9 | 10 | export default defineConfig(() => { 11 | return { 12 | build: { 13 | target: "es2020", 14 | lib: { 15 | entry: "./src/index.ts", 16 | formats: ["es", "cjs"], 17 | fileName: (format, entryName) => 18 | `${entryName}.qwik.${format === "es" ? "mjs" : "cjs"}`, 19 | }, 20 | rollupOptions: { 21 | output: { 22 | preserveModules: true, 23 | preserveModulesRoot: "src", 24 | }, 25 | // externalize deps that shouldn't be bundled into the library 26 | external: [ 27 | /^node:.*/, 28 | ...excludeAll(dependencies), 29 | ...excludeAll(peerDependencies), 30 | ], 31 | }, 32 | }, 33 | plugins: [qwikVite(), tsconfigPaths()], 34 | }; 35 | }); 36 | -------------------------------------------------------------------------------- /release.config.mjs: -------------------------------------------------------------------------------- 1 | /** 2 | * @type {import('semantic-release').GlobalConfig} 3 | */ 4 | export default { 5 | branches: ["main"], 6 | extends: "semantic-release-monorepo", 7 | plugins: [ 8 | [ 9 | "@semantic-release/commit-analyzer", 10 | { 11 | preset: "conventionalcommits", 12 | }, 13 | ], 14 | [ 15 | "@semantic-release/release-notes-generator", 16 | { 17 | preset: "conventionalcommits", 18 | }, 19 | ], 20 | [ 21 | "@semantic-release/npm", 22 | { 23 | npmPublish: false, 24 | }, 25 | ], 26 | [ 27 | "@semantic-release/exec", 28 | { 29 | prepareCmd: "yarn run build", 30 | }, 31 | ], 32 | "@semantic-release/changelog", 33 | [ 34 | "@semantic-release/git", 35 | { 36 | assets: ["package.json", "changelog"], 37 | message: 38 | "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}", 39 | }, 40 | ], 41 | [ 42 | "@semantic-release/github", 43 | { 44 | assets: [ 45 | { 46 | path: "dist/atomic-calendar-revive.js", 47 | }, 48 | ], 49 | }, 50 | ], 51 | ], 52 | }; 53 | -------------------------------------------------------------------------------- /apps/qwik-testing-library-e2e-tests/src/components/qwik-core/qwik-render.spec.tsx: -------------------------------------------------------------------------------- 1 | import { QwikRender } from "./qwik-render"; 2 | import { render, screen } from "@noma.to/qwik-testing-library"; 3 | import { userEvent } from "@testing-library/user-event"; 4 | 5 | describe("", () => { 6 | const aProp = "my-prop"; 7 | const initialProp = "qwik-prop"; 8 | const changedProp = "changed-prop"; 9 | 10 | it("should render prop value", async () => { 11 | await render(); 12 | 13 | expect(screen.getByText(aProp)).toBeInTheDocument(); 14 | }); 15 | 16 | describe("when props change", () => { 17 | it("should re-render", async () => { 18 | const user = userEvent.setup(); 19 | await render(); 20 | 21 | const changePropBtn = screen.getByRole("button", { 22 | name: /change prop/, 23 | }); 24 | await user.click(changePropBtn); 25 | 26 | expect(await screen.findByText(changedProp)).toBeInTheDocument(); 27 | }); 28 | }); 29 | 30 | it("should render a list of elements", async () => { 31 | await render(); 32 | 33 | expect(screen.findAllByRole("listitem")).resolves.toHaveLength(3); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /packages/qwik-mock/src/lib/qwik-mock.ts: -------------------------------------------------------------------------------- 1 | import { $, implicit$FirstArg } from "@builder.io/qwik"; 2 | import { vi } from "vitest"; 3 | 4 | export const mockQrl = () => { 5 | return $(vi.fn()); 6 | }; 7 | 8 | /** 9 | * @experimental 10 | * 11 | * Create a QRL mock that can be used in tests to verify interactions 12 | * 13 | * As Qwik is an async framework, you need to `resolve()` the mock before making your verifications. 14 | * And remember to clear the mocks before each test to start with a clean slate! 15 | * 16 | * @example 17 | * ```tsx 18 | * describe('', () => { 19 | * const onClickMock = mock$(() => {}); 20 | * 21 | * beforeEach(() => { 22 | * clearAllMocks(); 23 | * }); 24 | * 25 | * it('should call onClick$', async () => { 26 | * await render(); 27 | * 28 | * await userEvent.click(screen.getByRole('button')); 29 | * 30 | * await waitFor(() => expect(onClickMock.resolve()).resolves.toHaveBeenCalled()); 31 | * }); 32 | * }); 33 | * ``` 34 | */ 35 | export const mock$ = implicit$FirstArg(mockQrl); 36 | 37 | /** 38 | * Will call `.mockClear()` on all spies. This will clear mock history, but not reset its implementation to the 39 | * default one. 40 | */ 41 | export function clearAllMocks() { 42 | vi.clearAllMocks(); 43 | } 44 | -------------------------------------------------------------------------------- /packages/qwik-mock/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | browser: true, 5 | es2021: true, 6 | node: true, 7 | }, 8 | extends: [ 9 | "eslint:recommended", 10 | "plugin:@typescript-eslint/recommended", 11 | "plugin:qwik/recommended", 12 | ], 13 | parser: "@typescript-eslint/parser", 14 | parserOptions: { 15 | tsconfigRootDir: __dirname, 16 | project: ["./tsconfig.json"], 17 | ecmaVersion: 2021, 18 | sourceType: "module", 19 | ecmaFeatures: { 20 | jsx: true, 21 | }, 22 | }, 23 | plugins: ["@typescript-eslint"], 24 | rules: { 25 | "@typescript-eslint/no-explicit-any": "off", 26 | "@typescript-eslint/explicit-module-boundary-types": "off", 27 | "@typescript-eslint/no-inferrable-types": "off", 28 | "@typescript-eslint/no-non-null-assertion": "off", 29 | "@typescript-eslint/no-empty-interface": "off", 30 | "@typescript-eslint/no-namespace": "off", 31 | "@typescript-eslint/no-empty-function": "off", 32 | "@typescript-eslint/no-this-alias": "off", 33 | "@typescript-eslint/ban-types": "off", 34 | "@typescript-eslint/ban-ts-comment": "off", 35 | "prefer-spread": "off", 36 | "no-case-declarations": "off", 37 | "no-console": "off", 38 | "@typescript-eslint/no-unused-vars": ["error"], 39 | }, 40 | }; 41 | -------------------------------------------------------------------------------- /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "projectName": "qwik-testing-library", 3 | "projectOwner": "ianlet", 4 | "repoType": "github", 5 | "repoHost": "https://github.com", 6 | "files": [ 7 | "README.md" 8 | ], 9 | "imageSize": 100, 10 | "commit": false, 11 | "commitConvention": "none", 12 | "contributors": [ 13 | { 14 | "login": "ianlet", 15 | "name": "Ian Létourneau", 16 | "avatar_url": "https://avatars.githubusercontent.com/u/6018732?v=4", 17 | "profile": "https://noma.to/", 18 | "contributions": [ 19 | "code", 20 | "test", 21 | "ideas", 22 | "doc", 23 | "example" 24 | ] 25 | }, 26 | { 27 | "login": "brandonpittman", 28 | "name": "Brandon Pittman", 29 | "avatar_url": "https://avatars.githubusercontent.com/u/967145?v=4", 30 | "profile": "https://brandonpittman.com", 31 | "contributions": [ 32 | "doc" 33 | ] 34 | }, 35 | { 36 | "login": "thejackshelton", 37 | "name": "Jack Shelton", 38 | "avatar_url": "https://avatars.githubusercontent.com/u/104264123?v=4", 39 | "profile": "http://jackshelton.com", 40 | "contributions": [ 41 | "review" 42 | ] 43 | } 44 | ], 45 | "contributorsPerLine": 7, 46 | "linkToUsage": true, 47 | "commitType": "docs" 48 | } 49 | -------------------------------------------------------------------------------- /packages/qwik-testing-library/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | browser: true, 5 | es2021: true, 6 | node: true, 7 | }, 8 | extends: [ 9 | "eslint:recommended", 10 | "plugin:@typescript-eslint/recommended", 11 | "plugin:qwik/recommended", 12 | ], 13 | parser: "@typescript-eslint/parser", 14 | parserOptions: { 15 | tsconfigRootDir: __dirname, 16 | project: ["./tsconfig.json"], 17 | ecmaVersion: 2021, 18 | sourceType: "module", 19 | ecmaFeatures: { 20 | jsx: true, 21 | }, 22 | }, 23 | plugins: ["@typescript-eslint"], 24 | rules: { 25 | "@typescript-eslint/no-explicit-any": "off", 26 | "@typescript-eslint/explicit-module-boundary-types": "off", 27 | "@typescript-eslint/no-inferrable-types": "off", 28 | "@typescript-eslint/no-non-null-assertion": "off", 29 | "@typescript-eslint/no-empty-interface": "off", 30 | "@typescript-eslint/no-namespace": "off", 31 | "@typescript-eslint/no-empty-function": "off", 32 | "@typescript-eslint/no-this-alias": "off", 33 | "@typescript-eslint/ban-types": "off", 34 | "@typescript-eslint/ban-ts-comment": "off", 35 | "prefer-spread": "off", 36 | "no-case-declarations": "off", 37 | "no-console": "off", 38 | "@typescript-eslint/no-unused-vars": ["error"], 39 | }, 40 | }; 41 | -------------------------------------------------------------------------------- /apps/qwik-testing-library-e2e-tests/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | browser: true, 5 | es2021: true, 6 | node: true, 7 | }, 8 | extends: [ 9 | "eslint:recommended", 10 | "plugin:@typescript-eslint/recommended", 11 | "plugin:qwik/recommended", 12 | ], 13 | parser: "@typescript-eslint/parser", 14 | parserOptions: { 15 | tsconfigRootDir: __dirname, 16 | project: ["./tsconfig.json"], 17 | ecmaVersion: 2021, 18 | sourceType: "module", 19 | ecmaFeatures: { 20 | jsx: true, 21 | }, 22 | }, 23 | plugins: ["@typescript-eslint"], 24 | rules: { 25 | "@typescript-eslint/no-explicit-any": "off", 26 | "@typescript-eslint/explicit-module-boundary-types": "off", 27 | "@typescript-eslint/no-inferrable-types": "off", 28 | "@typescript-eslint/no-non-null-assertion": "off", 29 | "@typescript-eslint/no-empty-interface": "off", 30 | "@typescript-eslint/no-namespace": "off", 31 | "@typescript-eslint/no-empty-function": "off", 32 | "@typescript-eslint/no-this-alias": "off", 33 | "@typescript-eslint/ban-types": "off", 34 | "@typescript-eslint/ban-ts-comment": "off", 35 | "prefer-spread": "off", 36 | "no-case-declarations": "off", 37 | "no-console": "off", 38 | "@typescript-eslint/no-unused-vars": ["error"], 39 | }, 40 | }; 41 | -------------------------------------------------------------------------------- /apps/qwik-testing-library-e2e-tests/src/components/qwik-core/qwik-slot.spec.tsx: -------------------------------------------------------------------------------- 1 | import { render, screen, waitFor } from "@noma.to/qwik-testing-library"; 2 | import { userEvent } from "@testing-library/user-event"; 3 | import { QwikSlot } from "./qwik-slot"; 4 | 5 | describe("", () => { 6 | it("should render slot content", async () => { 7 | await render(default-slot); 8 | 9 | expect(screen.getByText("default-slot")).toBeInTheDocument(); 10 | }); 11 | 12 | it("should render named slot content", async () => { 13 | await render( 14 | 15 | named-slot 16 | , 17 | ); 18 | 19 | expect(screen.getByText("named-slot")).toBeInTheDocument(); 20 | }); 21 | 22 | it("should render conditional slot value", async () => { 23 | const user = userEvent.setup(); 24 | await render( 25 | 26 | 27 | , 28 | ); 29 | 30 | expect( 31 | screen.queryByRole("button", { name: "conditional-slot" }), 32 | ).not.toBeInTheDocument(); 33 | 34 | const button = screen.getByRole("button", { name: "update" }); 35 | await user.click(button); 36 | 37 | await waitFor(() => 38 | expect( 39 | screen.getByRole("button", { name: "conditional-slot" }), 40 | ).toBeInTheDocument(), 41 | ); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /apps/qwik-testing-library-e2e-tests/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import pkg from "./package.json"; 3 | import { qwikVite } from "@builder.io/qwik/optimizer"; 4 | import tsconfigPaths from "vite-tsconfig-paths"; 5 | 6 | const { dependencies = {}, peerDependencies = {} } = pkg as any; 7 | const makeRegex = (dep) => new RegExp(`^${dep}(/.*)?$`); 8 | const excludeAll = (obj) => Object.keys(obj).map(makeRegex); 9 | 10 | // Support both jsdom and happy-dom via TEST_DOM env variable 11 | const testEnvironment = process.env.TEST_DOM || "happy-dom"; 12 | 13 | export default defineConfig(() => { 14 | return { 15 | build: { 16 | target: "es2020", 17 | lib: { 18 | entry: "./src/index.ts", 19 | formats: ["es", "cjs"], 20 | fileName: (format, entryName) => 21 | `${entryName}.qwik.${format === "es" ? "mjs" : "cjs"}`, 22 | }, 23 | rollupOptions: { 24 | output: { 25 | preserveModules: true, 26 | preserveModulesRoot: "src", 27 | }, 28 | // externalize deps that shouldn't be bundled into the library 29 | external: [ 30 | /^node:.*/, 31 | ...excludeAll(dependencies), 32 | ...excludeAll(peerDependencies), 33 | ], 34 | }, 35 | }, 36 | plugins: [qwikVite(), tsconfigPaths()], 37 | test: { 38 | environment: testEnvironment, 39 | setupFiles: ["./vitest.setup.ts"], 40 | globals: true, 41 | }, 42 | }; 43 | }); 44 | -------------------------------------------------------------------------------- /apps/qwik-testing-library-e2e-tests/README.md: -------------------------------------------------------------------------------- 1 | # Qwik Library ⚡️ 2 | 3 | - [Qwik Docs](https://qwik.dev/) 4 | - [Discord](https://qwik.dev/chat) 5 | - [Qwik on GitHub](https://github.com/QwikDev/qwik) 6 | - [@QwikDev](https://twitter.com/QwikDev) 7 | - [Vite](https://vitejs.dev/) 8 | - [Partytown](https://partytown.builder.io/) 9 | - [Mitosis](https://github.com/BuilderIO/mitosis) 10 | - [Builder.io](https://www.builder.io/) 11 | 12 | --- 13 | 14 | ## Project Structure 15 | 16 | Inside your project, you'll see the following directories and files: 17 | 18 | ``` 19 | ├── public/ 20 | │ └── ... 21 | └── src/ 22 | ├── components/ 23 | │ └── ... 24 | └── index.ts 25 | ``` 26 | 27 | - `src/components`: Recommended directory for components. 28 | 29 | - `index.ts`: The entry point of your component library, make sure all the public components are exported from this file. 30 | 31 | ## Development 32 | 33 | Development mode uses [Vite's development server](https://vitejs.dev/). For Qwik during development, the `dev` command will also server-side render (SSR) the output. The client-side development modules are loaded by the browser. 34 | 35 | ``` 36 | pnpm dev 37 | ``` 38 | 39 | > Note: during dev mode, Vite will request many JS files, which does not represent a Qwik production build. 40 | 41 | ## Production 42 | 43 | The production build should generate the production build of your component library in (./lib) and the typescript type definitions in (./lib-types). 44 | 45 | ``` 46 | pnpm build 47 | ``` 48 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@noma.to/qwik-testing-library-root", 3 | "version": "0.0.0-semantically-released", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "toc": "doctoc README.md", 8 | "test": "pnpm -r build && pnpm -r test", 9 | "fmt": "pnpm -r fmt", 10 | "lint": "pnpm -r lint", 11 | "validate": "pnpm -r lint && pnpm -r validate", 12 | "contributors:add": "all-contributors add", 13 | "contributors:generate": "all-contributors generate", 14 | "build": "pnpm --filter '{packages/**}' run build", 15 | "release:prepare": "pnpm --filter '{packages/**}' run build && cp README.md packages/qwik-testing-library/README.md && cp LICENSE packages/qwik-testing-library/LICENSE && cp LICENSE packages/qwik-mock/LICENSE", 16 | "release": "pnpm --filter '{packages/**}' --workspace-concurrency=1 exec -- npx --no-install semantic-release -e semantic-release-monorepo", 17 | "prepare": "husky" 18 | }, 19 | "keywords": [ 20 | "testing", 21 | "testing-library", 22 | "qwik", 23 | "ui", 24 | "dom", 25 | "jsdom", 26 | "unit", 27 | "integration", 28 | "functional", 29 | "end-to-end", 30 | "e2e", 31 | "mock", 32 | "mocking", 33 | "vitest" 34 | ], 35 | "license": "MIT", 36 | "devDependencies": { 37 | "all-contributors-cli": "^6.26.1", 38 | "commitizen": "^4.3.1", 39 | "cz-conventional-changelog": "^3.3.0", 40 | "doctoc": "^2.2.1", 41 | "husky": "^9.1.7", 42 | "semantic-release": "^24.2.8", 43 | "semantic-release-monorepo": "^8.0.2" 44 | }, 45 | "config": { 46 | "commitizen": { 47 | "path": "./node_modules/cz-conventional-changelog" 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /packages/qwik-mock/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@noma.to/qwik-mock", 3 | "version": "0.0.0-semantically-released", 4 | "description": "Small utility to mock Qwik QRLs", 5 | "repository": "https://github.com/ianlet/qwik-testing-library", 6 | "homepage": "https://github.com/ianlet/qwik-testing-library", 7 | "license": "MIT", 8 | "main": "./lib/index.qwik.mjs", 9 | "qwik": "./lib/index.qwik.mjs", 10 | "types": "./lib-types/index.d.ts", 11 | "exports": { 12 | ".": { 13 | "types": "./lib-types/index.d.ts", 14 | "import": "./lib/index.qwik.mjs", 15 | "require": "./lib/index.qwik.cjs" 16 | } 17 | }, 18 | "files": [ 19 | "lib", 20 | "lib-types" 21 | ], 22 | "engines": { 23 | "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 24 | }, 25 | "private": false, 26 | "type": "module", 27 | "scripts": { 28 | "build": "pnpm build.lib && pnpm build.types", 29 | "build.lib": "vite build --mode lib", 30 | "build.types": "tsc --emitDeclarationOnly", 31 | "fmt": "prettier --write .", 32 | "fmt.check": "prettier --check .", 33 | "lint": "eslint \"src/**/*.ts*\"", 34 | "validate": "pnpm build" 35 | }, 36 | "devDependencies": { 37 | "@types/eslint": "8.56.10", 38 | "@types/node": "24.5.0", 39 | "@typescript-eslint/eslint-plugin": "8.44.0", 40 | "@typescript-eslint/parser": "8.45.0", 41 | "eslint": "8.57.1", 42 | "eslint-plugin-qwik": "1.16.0", 43 | "prettier": "3.6.2", 44 | "typescript": "5.9.2", 45 | "undici": "*", 46 | "vite": "7.1.7", 47 | "vite-tsconfig-paths": "^5.1.4", 48 | "vitest": "^3.2.4" 49 | }, 50 | "peerDependencies": { 51 | "@builder.io/qwik": ">= 1.12.0 < 2" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /packages/qwik-testing-library/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@noma.to/qwik-testing-library", 3 | "version": "0.0.0-semantically-released", 4 | "description": "Simple and complete Qwik testing utilities that encourage good testing practices.", 5 | "repository": "https://github.com/ianlet/qwik-testing-library", 6 | "homepage": "https://github.com/ianlet/qwik-testing-library", 7 | "license": "MIT", 8 | "main": "./lib/index.qwik.mjs", 9 | "qwik": "./lib/index.qwik.mjs", 10 | "types": "./lib-types/index.d.ts", 11 | "exports": { 12 | ".": { 13 | "types": "./lib-types/index.d.ts", 14 | "import": "./lib/index.qwik.mjs", 15 | "require": "./lib/index.qwik.cjs" 16 | } 17 | }, 18 | "files": [ 19 | "lib", 20 | "lib-types" 21 | ], 22 | "engines": { 23 | "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 24 | }, 25 | "private": false, 26 | "type": "module", 27 | "scripts": { 28 | "build": "pnpm build.lib && pnpm build.types", 29 | "build.lib": "vite build --mode lib", 30 | "build.types": "tsc --emitDeclarationOnly", 31 | "fmt": "prettier --write .", 32 | "fmt.check": "prettier --check .", 33 | "lint": "eslint \"src/**/*.ts*\"", 34 | "validate": "pnpm build" 35 | }, 36 | "devDependencies": { 37 | "@types/eslint": "8.56.10", 38 | "@types/node": "24.5.0", 39 | "@typescript-eslint/eslint-plugin": "8.44.0", 40 | "@typescript-eslint/parser": "8.45.0", 41 | "eslint": "8.57.1", 42 | "eslint-plugin-qwik": "1.16.0", 43 | "prettier": "3.6.2", 44 | "typescript": "5.9.2", 45 | "undici": "*", 46 | "vite": "7.1.7", 47 | "vite-tsconfig-paths": "^5.1.4", 48 | "vitest": "^3.2.4" 49 | }, 50 | "peerDependencies": { 51 | "@builder.io/qwik": ">= 1.12.0 < 2", 52 | "@testing-library/dom": ">= 10.1.0 < 11" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing guide 2 | 3 | ## Pull requests 4 | 5 | - Consider opening an issue before submitting a pull-request to avoid unnecessary work 6 | - Ensure pull request titles adhere to the [Conventional Commits][] specification 7 | 8 | [conventional commits]: https://www.conventionalcommits.org/ 9 | 10 | ## Release 11 | 12 | The module is released automatically from the `main` and `next` branches using [semantic-release-action][]. Version 13 | bumps and change logs are generated from the commit messages. 14 | 15 | [semantic-release-action]: https://github.com/cycjimmy/semantic-release-action 16 | 17 | ### Preview release 18 | 19 | If you would like to preview the release from a given branch, and... 20 | 21 | - You have push access to the repository 22 | - The branch exists in GitHub 23 | 24 | ...you can preview the next release version and changelog using: 25 | 26 | ```shell 27 | pnpm run preview-release 28 | ``` 29 | 30 | ## Development setup 31 | 32 | After cloning the repository, install the project's dependencies and run the `validate` script to run all checks and 33 | tests to verify your setup. 34 | 35 | ```shell 36 | pnpm install 37 | pnpm validate 38 | ``` 39 | 40 | ### Lint and format 41 | 42 | Run auto-formatting to ensure any changes adhere to the code style of the repository: 43 | 44 | ```shell 45 | pnpm -r fmt 46 | ``` 47 | 48 | To run lint and format checks without making any changes: 49 | 50 | ```shell 51 | pnpm -r fmt.check 52 | ``` 53 | 54 | ### Test 55 | 56 | Run unit tests once: 57 | 58 | ```shell 59 | pnpm -r test 60 | ``` 61 | 62 | ### Docs 63 | 64 | Use the `toc` script to ensure the README's table of contents is up to date: 65 | 66 | ```shell 67 | pnpm toc 68 | ``` 69 | 70 | Use `contributors:add` to add a contributor to the README: 71 | 72 | ```shell 73 | pnpm contributors:add 74 | ``` 75 | 76 | Use `contributors:generate` to ensure the README's contributor list is up to date: 77 | 78 | ```shell 79 | pnpm contributors:generate 80 | ``` -------------------------------------------------------------------------------- /apps/qwik-testing-library-e2e-tests/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-qwik-library-name", 3 | "version": "0.0.0-semantically-released", 4 | "description": "Create a Qwik library", 5 | "main": "./lib/index.qwik.mjs", 6 | "qwik": "./lib/index.qwik.mjs", 7 | "types": "./lib-types/index.d.ts", 8 | "exports": { 9 | ".": { 10 | "types": "./lib-types/index.d.ts", 11 | "import": "./lib/index.qwik.mjs", 12 | "require": "./lib/index.qwik.cjs" 13 | } 14 | }, 15 | "files": [ 16 | "lib", 17 | "lib-types" 18 | ], 19 | "engines": { 20 | "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 21 | }, 22 | "private": false, 23 | "type": "module", 24 | "scripts": { 25 | "build": "qwik build", 26 | "build.lib": "vite build --mode lib", 27 | "build.types": "tsc --emitDeclarationOnly", 28 | "dev": "vite --mode ssr", 29 | "dev.debug": "node --inspect-brk ./node_modules/vite/bin/vite.js --mode ssr --force", 30 | "fmt": "prettier --write .", 31 | "fmt.check": "prettier --check .", 32 | "lint": "eslint \"src/**/*.ts*\"", 33 | "start": "vite --open --mode ssr", 34 | "test": "vitest run components", 35 | "validate": "pnpm test", 36 | "qwik": "qwik" 37 | }, 38 | "devDependencies": { 39 | "@builder.io/qwik": "^1.16.0", 40 | "@noma.to/qwik-mock": "workspace:*", 41 | "@noma.to/qwik-testing-library": "workspace:*", 42 | "@testing-library/jest-dom": "^6.9.0", 43 | "@testing-library/user-event": "^14.6.1", 44 | "@types/eslint": "8.56.10", 45 | "@types/node": "24.5.0", 46 | "@typescript-eslint/eslint-plugin": "8.44.0", 47 | "@typescript-eslint/parser": "8.45.0", 48 | "@vitest/ui": "^3.2.4", 49 | "eslint": "8.57.1", 50 | "eslint-plugin-qwik": "1.16.0", 51 | "happy-dom": "^20.0.11", 52 | "jsdom": "^26.1.0", 53 | "prettier": "3.6.2", 54 | "typescript": "5.9.2", 55 | "undici": "*", 56 | "vite": "7.1.7", 57 | "vite-tsconfig-paths": "^5.1.4", 58 | "vitest": "^3.2.4" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug.yml: -------------------------------------------------------------------------------- 1 | name: 🐞 Bug Report 2 | description: Something does not work or is flaky! let us know! 3 | labels: [ 'bug', 'STATUS-1: needs triage' ] 4 | title: '[🐞]' 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | Thanks for taking the time to fill out this bug report! 10 | 11 | - type: textarea 12 | id: description 13 | attributes: 14 | description: 'A clear and concise description of what you expected to happen instead. If you intend to submit a PR for this issue, tell us in the description. Thanks!' 15 | label: Describe the bug 16 | placeholder: I am doing ... What I expect is ... What actually happening is ... 17 | 18 | validations: 19 | required: true 20 | 21 | - type: input 22 | id: reproduction 23 | attributes: 24 | label: Reproduction 25 | description: Please provide a link via [qwik.new](https://qwik.new/) or a link to a repo that can reproduce the problem you ran into. `npm create qwik@latest` can be used as a starter template. A [minimal reproduction](https://stackoverflow.com/help/minimal-reproducible-example) is required ([Why?](https://antfu.me/posts/why-reproductions-are-required)). If a report is vague (e.g. just a generic error message) and has no reproduction, it will receive a "need reproduction" label. If no reproduction is provided after 3 days, it will be auto-closed. 26 | placeholder: Reproduction URL 27 | validations: 28 | required: true 29 | 30 | - type: textarea 31 | id: reproduction-steps 32 | attributes: 33 | label: Steps to reproduce 34 | description: Please provide any reproduction steps that may need to be described. E.g. if it happens only when running the dev or build script make sure it's clear which one to use. 35 | placeholder: Run `npm install` followed by `npm run dev` 36 | 37 | - type: textarea 38 | id: system-info 39 | attributes: 40 | label: System Info 41 | description: Output of `npx envinfo --system --npmPackages '{vite,undici,typescript,@builder.io/*}' --binaries --browsers` 42 | render: shell 43 | placeholder: System, Binaries, Browsers 44 | validations: 45 | required: true 46 | 47 | - type: textarea 48 | id: additional_information 49 | attributes: 50 | label: Additional Information 51 | validations: 52 | required: false -------------------------------------------------------------------------------- /apps/qwik-testing-library-e2e-tests/src/components/counter.spec.tsx: -------------------------------------------------------------------------------- 1 | import { render, screen, waitFor } from "@noma.to/qwik-testing-library"; 2 | import { clearAllMocks, mock$ } from "@noma.to/qwik-mock"; 3 | import { userEvent } from "@testing-library/user-event"; 4 | import { Counter } from "./counter"; 5 | 6 | describe("", () => { 7 | const onChangeMock = mock$(() => {}); 8 | 9 | beforeEach(() => { 10 | clearAllMocks(); 11 | }); 12 | 13 | it("should start at 0 by default", async () => { 14 | await render(); 15 | 16 | expect(screen.getByText("Counter: 0")).toBeInTheDocument(); 17 | }); 18 | 19 | describe("on increment", () => { 20 | it("should increase by 1", async () => { 21 | const user = userEvent.setup(); 22 | await render(); 23 | 24 | const incrementBtn = screen.getByRole("button", { name: "Increment" }); 25 | await user.click(incrementBtn); 26 | 27 | expect(await screen.findByText("Counter: 1")).toBeInTheDocument(); 28 | }); 29 | 30 | it("should call onChange$", async () => { 31 | const user = userEvent.setup(); 32 | await render(); 33 | 34 | const incrementBtn = screen.getByRole("button", { name: "Increment" }); 35 | await user.click(incrementBtn); 36 | 37 | await waitFor(() => 38 | expect(onChangeMock.resolve()).resolves.toHaveBeenCalledWith(1), 39 | ); 40 | }); 41 | }); 42 | 43 | describe("on decrement", () => { 44 | it("should decrease by 1", async () => { 45 | const user = userEvent.setup(); 46 | await render(); 47 | 48 | const decrementBtn = screen.getByRole("button", { name: "Decrement" }); 49 | await user.click(decrementBtn); 50 | 51 | expect(await screen.findByText("Counter: -1")).toBeInTheDocument(); 52 | }); 53 | 54 | it("should call onChange$", async () => { 55 | const user = userEvent.setup(); 56 | await render(); 57 | 58 | const decrementBtn = screen.getByRole("button", { name: "Decrement" }); 59 | await user.click(decrementBtn); 60 | 61 | await waitFor(() => 62 | expect(onChangeMock.resolve()).resolves.toHaveBeenCalledWith(-1), 63 | ); 64 | }); 65 | }); 66 | }); 67 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of 9 | experience, nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | - Using welcoming and inclusive language 18 | - Being respectful of differing viewpoints and experiences 19 | - Gracefully accepting constructive criticism 20 | - Focusing on what is best for the community 21 | - Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | - The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | - Trolling, insulting/derogatory comments, and personal or political attacks 28 | - Public or private harassment 29 | - Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | - Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or reject 41 | comments, commits, code, wiki edits, issues, and other contributions that are 42 | not aligned to this Code of Conduct, or to ban temporarily or permanently any 43 | contributor for other behaviors that they deem inappropriate, threatening, 44 | offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at hello@noma.to. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an 62 | incident. Further details of specific enforcement policies may be posted 63 | separately. 64 | 65 | Project maintainers who do not follow or enforce the Code of Conduct in good 66 | faith may face temporary or permanent repercussions as determined by other 67 | members of the project's leadership. 68 | 69 | ## Attribution 70 | 71 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 72 | version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 73 | 74 | [homepage]: http://contributor-covenant.org 75 | 76 | [version]: http://contributor-covenant.org/version/1/4/ -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | schedule: 9 | # Tuesdays at 14:45 UTC (10:45 EST) 10 | - cron: 45 14 * * 1 11 | 12 | permissions: 13 | contents: read # for checkout 14 | 15 | concurrency: 16 | group: ${{ github.workflow }}-${{ github.ref }} 17 | cancel-in-progress: true 18 | 19 | jobs: 20 | main: 21 | # ignore all-contributors PRs 22 | if: ${{ !contains(github.head_ref, 'all-contributors') }} 23 | name: Node ${{ matrix.node }}, Qwik ${{ matrix.qwik }}, ${{ matrix.dom }}, ${{ matrix.check }} 24 | runs-on: ubuntu-latest 25 | 26 | strategy: 27 | fail-fast: false 28 | matrix: 29 | node: ["18", "20", "22"] 30 | qwik: ["1.12", "1.14.1", "1.15", "1.16", "1.17"] 31 | dom: ["jsdom", "happy-dom"] 32 | check: ["test"] 33 | include: 34 | - { node: "22", qwik: "1.17", check: "lint" } 35 | 36 | steps: 37 | - name: ⬇️ Checkout repo 38 | uses: actions/checkout@v6 39 | with: 40 | fetch-depth: 0 41 | 42 | - name: 🧱 Setup pnpm 43 | uses: pnpm/action-setup@v4 44 | with: 45 | version: 9 46 | run_install: false 47 | 48 | - name: ⎔ Setup node 49 | uses: actions/setup-node@v6 50 | with: 51 | node-version: ${{ matrix.node }} 52 | cache: "pnpm" 53 | 54 | - name: 📥 Download deps 55 | run: | 56 | pnpm install 57 | pnpm --filter "{packages/qwik-testing-library}" install @builder.io/qwik@${{ matrix.qwik }} 58 | pnpm --filter "{packages/qwik-mock}" install @builder.io/qwik@${{ matrix.qwik }} 59 | pnpm --filter "{apps/qwik-testing-library-e2e-tests}" install @builder.io/qwik@${{ matrix.qwik }} 60 | 61 | - name: ▶️ Run ${{ matrix.check }} 62 | run: pnpm ${{ matrix.check }} 63 | env: 64 | TEST_DOM: ${{ matrix.dom }} 65 | 66 | build: 67 | runs-on: ubuntu-latest 68 | steps: 69 | - name: ⬇️ Checkout repo 70 | uses: actions/checkout@v6 71 | with: 72 | fetch-depth: 0 73 | 74 | - name: 🧱 Setup pnpm 75 | uses: pnpm/action-setup@v4 76 | with: 77 | version: 9 78 | run_install: false 79 | 80 | - name: ⎔ Setup node 81 | uses: actions/setup-node@v6 82 | with: 83 | node-version: 20 84 | cache: "pnpm" 85 | 86 | - name: 📥 Download deps 87 | run: pnpm install 88 | 89 | - name: 🏗️ Build packages 90 | run: pnpm build 91 | 92 | release: 93 | name: Release 94 | needs: [main, build] 95 | runs-on: ubuntu-latest 96 | permissions: 97 | contents: write # to be able to publish a GitHub release 98 | issues: write # to be able to comment on released issues 99 | pull-requests: write # to be able to comment on released pull requests 100 | id-token: write # to enable use of OIDC for npm provenance 101 | steps: 102 | - name: ⬇️ Checkout repo 103 | uses: actions/checkout@v6 104 | with: 105 | fetch-depth: 0 106 | 107 | - name: 🧱 Setup pnpm 108 | uses: pnpm/action-setup@v4 109 | with: 110 | version: 9 111 | run_install: false 112 | 113 | - name: ⎔ Setup node 114 | uses: actions/setup-node@v6 115 | with: 116 | node-version: 20 117 | cache: "pnpm" 118 | 119 | - name: 📥 Download deps 120 | run: pnpm install 121 | 122 | - name: 🏗️ Build packages 123 | run: pnpm release:prepare 124 | 125 | - name: 🚀 Release 126 | env: 127 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 128 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 129 | run: pnpm release 130 | -------------------------------------------------------------------------------- /packages/qwik-testing-library/src/lib/qwik-testing-library.tsx: -------------------------------------------------------------------------------- 1 | import { getQueriesForElement, prettyDOM } from "@testing-library/dom"; 2 | import { JSXOutput } from "@builder.io/qwik"; 3 | import { getQwikLoaderScript } from "@builder.io/qwik/server"; 4 | import type { ComponentRef, Options, Result } from "./types"; 5 | 6 | // Patch HTMLTemplateElement.childNodes for happy-dom compatibility 7 | // 8 | // Qwik's VirtualElementImpl uses a