├── .editorconfig
├── .github
├── dependabot.yml
├── scripts
│ └── updateDeps.mjs
└── workflows
│ ├── expense.yml
│ ├── test.yaml
│ └── update.yml
├── .gitignore
├── .nvmrc
├── LICENSE
├── README.md
├── enhance
├── .arc
├── .gitignore
├── app
│ ├── elements
│ │ ├── my-header.mjs
│ │ └── my-header.test.js
│ └── pages
│ │ └── index.html
├── package.json
├── prefs.arc
├── public
│ ├── axol-welcome.svg
│ ├── discord-logo.svg
│ ├── enhance-type.svg
│ ├── favicon.svg
│ └── styles.css
└── wdio.conf.js
├── framework-comparisons
├── .gitignore
├── README.md
├── cypress.config.ts
├── cypress
│ └── support
│ │ ├── component-index.html
│ │ └── component.ts
├── index.html
├── package-lock.json
├── package.json
├── playwright.config.ts
├── playwright
│ ├── index.html
│ └── index.ts
├── public
│ └── vite.svg
├── src
│ ├── App.css
│ ├── App.tsx
│ ├── assets
│ │ └── react.svg
│ ├── components
│ │ ├── Card.cy.tsx
│ │ ├── Card.pw.tsx
│ │ ├── Card.test.tsx
│ │ ├── Card.tsx
│ │ ├── Card.wdio.tsx
│ │ ├── Input.cy.tsx
│ │ ├── Input.test.tsx
│ │ ├── Input.tsx
│ │ ├── Input.wdio.tsx
│ │ ├── InstantSearch.css
│ │ ├── InstantSearch.test.tsx
│ │ ├── InstantSearch.tsx
│ │ ├── InstantSearch.wdio.tsx
│ │ ├── Zoom.cy.tsx
│ │ ├── Zoom.pw.tsx
│ │ ├── Zoom.tsx
│ │ ├── Zoom.wdio.tsx
│ │ ├── __fixtures__
│ │ │ └── algolia.json
│ │ └── input.pw.tsx
│ ├── constants.ts
│ ├── index.css
│ ├── main.tsx
│ └── vite-env.d.ts
├── tsconfig.json
├── tsconfig.node.json
├── vite.config.ts
├── vitest.conf.ts
└── wdio.conf.ts
├── ionic-react
├── .browserslistrc
├── .eslintrc.js
├── .gitignore
├── .vscode
│ └── extensions.json
├── capacitor.config.ts
├── index.html
├── ionic.config.json
├── package-lock.json
├── package.json
├── public
│ ├── favicon.png
│ └── manifest.json
├── resources
│ ├── icon.png
│ └── splash.png
├── src
│ ├── App.test.tsx
│ ├── App.tsx
│ ├── components
│ │ ├── ExploreContainer.css
│ │ └── ExploreContainer.tsx
│ ├── main.tsx
│ ├── pages
│ │ ├── Tab1.css
│ │ ├── Tab1.tsx
│ │ ├── Tab2.css
│ │ ├── Tab2.tsx
│ │ ├── Tab3.css
│ │ └── Tab3.tsx
│ ├── theme
│ │ └── variables.css
│ └── vite-env.d.ts
├── tsconfig.json
├── tsconfig.node.json
├── vite.config.ts
└── wdio.conf.ts
├── lit-typescript-vite
├── .gitignore
├── index.html
├── package-lock.json
├── package.json
├── public
│ └── vite.svg
├── src
│ ├── assets
│ │ └── lit.svg
│ ├── index.css
│ ├── my-element.ts
│ ├── tests
│ │ └── my-element.test.ts
│ └── vite-env.d.ts
├── tsconfig.json
├── tsconfig.node.json
├── vite.config.ts
└── wdio.conf.ts
├── nextjs
├── .gitignore
├── README.md
├── eslint.config.mjs
├── next.config.ts
├── package-lock.json
├── package.json
├── postcss.config.mjs
├── public
│ ├── file.svg
│ ├── globe.svg
│ ├── next.svg
│ ├── vercel.svg
│ └── window.svg
├── src
│ └── app
│ │ ├── favicon.ico
│ │ ├── globals.css
│ │ ├── layout.tsx
│ │ └── page.tsx
├── tailwind.config.ts
├── tsconfig.json
└── wdio.conf.mts
├── nuxt
├── .env.example
├── .gitignore
├── .npmrc
├── README.md
├── app.config.ts
├── app.vue
├── assets
│ └── css
│ │ └── main.css
├── components
│ ├── AppAlerts.vue
│ ├── AppFileUpload.vue
│ ├── Date.test.ts
│ ├── Date.vue
│ ├── FormField.vue
│ ├── Money.test.ts
│ ├── Money.vue
│ ├── NavBar.vue
│ ├── Pagination.vue
│ ├── ProjectCard.vue
│ ├── ProjectPledgeForm.vue
│ ├── ProjectsDetails.vue
│ ├── ProjectsForm.vue
│ ├── ProjectsList.vue
│ └── TimeAgo.vue
├── composables
│ ├── useAlerts.ts
│ ├── useCategories.ts
│ ├── useFilePreview.ts
│ ├── usePaginator.ts
│ ├── useProjects.ts
│ └── useTypedSupabaseClient.ts
├── middleware
│ └── auth.ts
├── nuxt.config.ts
├── package-lock.json
├── package.json
├── pages
│ ├── categories
│ │ └── [uuid].vue
│ ├── connect-wallet.vue
│ ├── index.vue
│ ├── login.vue
│ ├── logout.vue
│ ├── profile.vue
│ ├── projects
│ │ ├── [uuid].vue
│ │ └── create.vue
│ └── register.vue
├── public
│ └── favicon.ico
├── server
│ └── tsconfig.json
├── supabase
│ ├── .gitignore
│ ├── config.toml
│ ├── functions
│ │ └── .vscode
│ │ │ └── settings.json
│ ├── migrations
│ │ └── 20230717154337_initial_schema.sql
│ ├── schema.ts
│ └── seed.sql
├── tailwind.config.js
├── tsconfig.json
├── types
│ └── index.ts
├── utils
│ └── index.ts
├── wdio.conf.ts
└── yarn.lock
├── preact-typescript-vite
├── .gitignore
├── index.html
├── package-lock.json
├── package.json
├── public
│ └── vite.svg
├── src
│ ├── app.css
│ ├── app.tsx
│ ├── assets
│ │ └── preact.svg
│ ├── index.css
│ ├── main.tsx
│ ├── tests
│ │ └── HelloWorld.test.tsx
│ └── vite-env.d.ts
├── tsconfig.json
├── tsconfig.node.json
├── vite.config.ts
└── wdio.conf.ts
├── react-typescript-vite
├── .gitignore
├── index.html
├── package-lock.json
├── package.json
├── public
│ └── vite.svg
├── src
│ ├── App.css
│ ├── App.tsx
│ ├── assets
│ │ └── react.svg
│ ├── components
│ │ ├── Button.tsx
│ │ ├── InputField.tsx
│ │ └── LoginForm.tsx
│ ├── index.css
│ ├── main.tsx
│ ├── tests
│ │ ├── HelloWorld.test.tsx
│ │ └── LoginForm.test.tsx
│ └── vite-env.d.ts
├── tsconfig.json
├── tsconfig.node.json
├── vite.config.ts
└── wdio.conf.ts
├── solidjs-typescript-vite
├── .gitignore
├── README.md
├── index.html
├── package-lock.json
├── package.json
├── src
│ ├── App.module.css
│ ├── App.tsx
│ ├── assets
│ │ └── favicon.ico
│ ├── components
│ │ ├── counter.tsx
│ │ ├── modal.css
│ │ └── modal.tsx
│ ├── directives
│ │ └── click-outside.tsx
│ ├── index.css
│ ├── index.tsx
│ ├── logo.svg
│ └── tests
│ │ ├── counter.test.tsx
│ │ ├── example.test.tsx
│ │ └── modal.test.tsx
├── tsconfig.json
├── vite.config.ts
├── wdio.conf.ts
└── wdio.sauce.conf.ts
├── stencil-component-starter
├── .editorconfig
├── .gitignore
├── .prettierrc.json
├── LICENSE
├── __snapshots__
│ └── MyComponent-chrome-1200x1551-dpr-2.png
├── package-lock.json
├── package.json
├── readme.md
├── src
│ ├── components.d.ts
│ ├── components
│ │ └── my-component
│ │ │ ├── my-component.css
│ │ │ ├── my-component.test.tsx
│ │ │ ├── my-component.tsx
│ │ │ └── readme.md
│ ├── index.html
│ ├── index.ts
│ └── utils
│ │ ├── utils.test.ts
│ │ └── utils.ts
├── stencil.config.ts
├── tsconfig.json
└── wdio.conf.ts
├── svelte-typescript-vite
├── .gitignore
├── .vscode
│ └── extensions.json
├── README.md
├── index.html
├── package-lock.json
├── package.json
├── public
│ └── vite.svg
├── src
│ ├── App.svelte
│ ├── app.css
│ ├── assets
│ │ └── svelte.svg
│ ├── lib
│ │ ├── CircleDrawer.svelte
│ │ ├── Counter.svelte
│ │ ├── Hoverable.svelte
│ │ └── Longpress.svelte
│ ├── main.ts
│ ├── tests
│ │ ├── CircleDrawer.test.ts
│ │ ├── Counter.test.ts
│ │ ├── Hoverable.test.ts
│ │ ├── Longpress.test.ts
│ │ └── __fixtures__
│ │ │ └── Hoverable.svelte
│ └── vite-env.d.ts
├── svelte.config.js
├── tsconfig.json
├── tsconfig.node.json
├── vite.config.ts
└── wdio.conf.ts
└── vue-typescript-vite
├── .gitignore
├── .vscode
└── extensions.json
├── README.md
├── index.html
├── package-lock.json
├── package.json
├── public
└── vite.svg
├── src
├── App.vue
├── assets
│ └── vue.svg
├── components
│ ├── CircleDrawer.vue
│ └── HelloWorld.vue
├── main.ts
├── style.css
├── tests
│ ├── CircleDrawer.test.ts
│ └── HelloWorld.test.ts
└── vite-env.d.ts
├── tsconfig.json
├── tsconfig.node.json
├── vite.config.ts
└── wdio.conf.ts
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain consistent
2 | # coding styles between different editors and IDEs
3 | # editorconfig.org
4 |
5 | root = true
6 |
7 | [*]
8 |
9 | indent_style = space
10 | indent_size = 2
11 |
12 | end_of_line = lf
13 | charset = utf-8
14 | trim_trailing_whitespace = true
15 | insert_final_newline = true
16 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | ---
2 | version: 2
3 |
4 | updates:
5 | - package-ecosystem: npm
6 | directory: "/"
7 | schedule:
8 | interval: weekly
9 | time: "11:00"
10 | open-pull-requests-limit: 10
11 | versioning-strategy: increase-if-necessary
12 |
13 | - package-ecosystem: "github-actions"
14 | directory: "/"
15 | schedule:
16 | interval: "weekly"
17 | time: "11:00"
18 |
--------------------------------------------------------------------------------
/.github/scripts/updateDeps.mjs:
--------------------------------------------------------------------------------
1 | import fs from 'node:fs/promises'
2 | import url from 'node:url'
3 | import path from 'node:path'
4 | import cp from 'node:child_process'
5 |
6 | const __dirname = url.fileURLToPath(new URL('.', import.meta.url))
7 | const root = path.resolve(__dirname, '..', '..')
8 |
9 | function exec (command, framework) {
10 | const prefix = framework ? `[${framework}]: ` : ''
11 | const cwd = framework
12 | ? path.resolve(root, framework)
13 | : process.cwd()
14 |
15 | return new Promise((resolve, reject) => {
16 | console.log(`▶️ ${prefix}${command}`);
17 | const res = cp.exec(command, { cwd }, (err, stdout) => {
18 | if (err) return reject(err)
19 | console.log(prefix + stdout);
20 | return resolve()
21 | })
22 | })
23 | }
24 |
25 | const hasNCUInstalled = await exec('which ncu').then(() => true, () => false)
26 | if (!hasNCUInstalled) {
27 | throw new Error(`Missing global dependency "ncu", please install via "npm i -g npm-check-updates"`)
28 | }
29 |
30 | const frameworkDirs = await Promise.all((await fs.readdir(root))
31 | .filter((entry) => !entry.startsWith('.'))
32 | .map(async (entry) => [entry, (await fs.stat(entry)).isDirectory()])
33 | ).then((res) => res
34 | .filter(([, isDirectory]) => isDirectory)
35 | .map(([entry]) => entry)
36 | .filter((entry) => (
37 | !entry.startsWith('framework-comparison') &&
38 | /**
39 | * fails update due to:
40 | * npm ERR! ERESOLVE unable to resolve dependency tree
41 | * npm ERR!
42 | * npm ERR! While resolving: ionic-react@0.0.1
43 | * npm ERR! Found: react-router@6.21.2
44 | * npm ERR! node_modules/react-router
45 | * npm ERR! react-router@"^6.21.2" from the root project
46 | * npm ERR!
47 | * npm ERR! Could not resolve dependency:
48 | * npm ERR! peer react-router@"^5.0.1" from @ionic/react-router@7.6.4
49 | * npm ERR! node_modules/@ionic/react-router
50 | * npm ERR! @ionic/react-router@"^7.6.4" from the root project
51 | */
52 | entry !== 'ionic-react'
53 | )))
54 |
55 | await Promise.all(frameworkDirs.map(async (framework) => {
56 | console.log(`🪄 Update dependencies for ${framework}`)
57 | await fs.unlink(path.join(root, framework, 'node_modules'))
58 | .catch((err) => { /* ignore */ })
59 | await exec(`rm -fr ./node_modules package-lock.json`, framework)
60 | await exec(`ncu -u`, framework)
61 | await exec(`npm i`, framework)
62 | }))
63 |
--------------------------------------------------------------------------------
/.github/workflows/expense.yml:
--------------------------------------------------------------------------------
1 | name: Expense Contribution
2 |
3 | on:
4 | workflow_dispatch:
5 | inputs:
6 | prNumber:
7 | description: "Number of the PR (without #)"
8 | required: true
9 | amount:
10 | description: "The expense amount you like to grant for the contribution in $"
11 | required: true
12 | type: choice
13 | options:
14 | - 15
15 | - 25
16 | - 35
17 | - 50
18 | - 100
19 | - 150
20 | - 200
21 | - 250
22 | - 300
23 | - 350
24 | - 400
25 | - 450
26 | - 500
27 | - 550
28 | - 600
29 | - 650
30 | - 700
31 | - 750
32 | - 800
33 | - 850
34 | - 900
35 | - 950
36 | - 1000
37 |
38 | jobs:
39 | authorize:
40 | runs-on: ubuntu-latest
41 | steps:
42 | - uses: octokit/request-action@v2.3.1
43 | with:
44 | route: GET /orgs/:organisation/teams/:team/memberships/${{ github.actor }}
45 | team: technical-steering-committee
46 | organisation: webdriverio
47 | env:
48 | GITHUB_TOKEN: ${{ secrets.WDIO_BOT_GITHUB_TOKEN }}
49 | expense:
50 | permissions:
51 | contents: write
52 | id-token: write
53 | needs: [authorize]
54 | runs-on: ubuntu-latest
55 | steps:
56 | - name: Run Expense Flow
57 | uses: webdriverio/expense-action@v1
58 | with:
59 | prNumber: ${{ github.event.inputs.prNumber }}
60 | amount: ${{ github.event.inputs.amount }}
61 | env:
62 | RESEND_API_KEY: ${{ secrets.RESEND_API_KEY }}
63 | GH_TOKEN: ${{ secrets.WDIO_BOT_GITHUB_TOKEN }}
64 |
--------------------------------------------------------------------------------
/.github/workflows/test.yaml:
--------------------------------------------------------------------------------
1 | name: Test
2 | on: [push, pull_request]
3 | jobs:
4 | test:
5 | runs-on: ubuntu-latest
6 | strategy:
7 | matrix:
8 | exampleDir:
9 | - lit-typescript-vite
10 | - preact-typescript-vite
11 | - react-typescript-vite
12 | - svelte-typescript-vite
13 | - vue-typescript-vite
14 | - solidjs-typescript-vite
15 | - enhance
16 | # disable due to: https://github.com/webdriverio/webdriverio/issues/14160
17 | # - nuxt
18 | # - nextjs
19 | - stencil-component-starter
20 | - ionic-react
21 | steps:
22 | - name: Checkout
23 | uses: actions/checkout@v4
24 | - uses: actions/setup-node@v4
25 | with:
26 | node-version: 18
27 | - name: Install
28 | run: npm install
29 | working-directory: ${{ matrix.exampleDir }}
30 | - name: Test
31 | run: npm run wdio
32 | working-directory: ${{ matrix.exampleDir }}
33 | - uses: actions/upload-artifact@v4
34 | if: failure()
35 | with:
36 | name: logs
37 | path: ${{ matrix.exampleDir }}/logs
38 |
--------------------------------------------------------------------------------
/.github/workflows/update.yml:
--------------------------------------------------------------------------------
1 | # this workflow merges requests from Dependabot if tests are passing
2 | # ref https://docs.github.com/en/code-security/dependabot/working-with-dependabot/automating-dependabot-with-github-actions
3 | # and https://github.com/dependabot/fetch-metadata
4 | name: Auto-merge
5 |
6 | # `pull_request_target` means this uses code in the base branch, not the PR.
7 | on: pull_request_target
8 |
9 | # Dependabot PRs' tokens have read permissions by default and thus we must enable write permissions.
10 | permissions:
11 | contents: write
12 | pull-requests: write
13 |
14 | jobs:
15 | dependencies:
16 | runs-on: ubuntu-latest
17 | if: github.actor == 'dependabot[bot]'
18 |
19 | steps:
20 | - name: Fetch PR metadata
21 | id: metadata
22 | uses: dependabot/fetch-metadata@v2.1.0
23 | with:
24 | github-token: ${{ secrets.GITHUB_TOKEN }}
25 |
26 | - name: Wait for PR CI
27 | # Don't merge updates to GitHub Actions versions automatically.
28 | # (Some repos may wish to limit by version range (major/minor/patch), or scope (dep vs dev-dep), too.)
29 | if: contains(steps.metadata.outputs.package-ecosystem, 'npm')
30 | uses: lewagon/wait-on-check-action@v1.3.4
31 | with:
32 | ref: ${{ github.event.pull_request.head.sha }}
33 | repo-token: ${{ secrets.GITHUB_TOKEN }}
34 | wait-interval: 30 # seconds
35 | running-workflow-name: dependencies # wait for all checks except this one
36 | allowed-conclusions: success # all other checks must pass, being skipped or cancelled is not sufficient
37 |
38 | - name: Auto-merge dependabot PRs
39 | # Don't merge updates to GitHub Actions versions automatically.
40 | # (Some repos may wish to limit by version range (major/minor/patch), or scope (dep vs dev-dep), too.)
41 | if: contains(steps.metadata.outputs.package-ecosystem, 'npm')
42 | env:
43 | PR_URL: ${{ github.event.pull_request.html_url }}
44 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
45 | # The "auto" flag will only merge once all of the target branch's required checks
46 | # are met. Configure those in the "branch protection" settings for each repo.
47 | run: gh pr merge --auto --squash "$PR_URL"
48 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 |
9 | # Diagnostic reports (https://nodejs.org/api/report.html)
10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
11 |
12 | # Runtime data
13 | pids
14 | *.pid
15 | *.seed
16 | *.pid.lock
17 |
18 | # Directory for instrumented libs generated by jscoverage/JSCover
19 | lib-cov
20 |
21 | # Coverage directory used by tools like istanbul
22 | coverage
23 | *.lcov
24 |
25 | # nyc test coverage
26 | .nyc_output
27 |
28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
29 | .grunt
30 |
31 | # Bower dependency directory (https://bower.io/)
32 | bower_components
33 |
34 | # node-waf configuration
35 | .lock-wscript
36 |
37 | # Compiled binary addons (https://nodejs.org/api/addons.html)
38 | build/Release
39 |
40 | # Dependency directories
41 | node_modules/
42 | jspm_packages/
43 |
44 | # TypeScript v1 declaration files
45 | typings/
46 |
47 | # TypeScript cache
48 | *.tsbuildinfo
49 |
50 | # Optional npm cache directory
51 | .npm
52 |
53 | # Optional eslint cache
54 | .eslintcache
55 |
56 | # Microbundle cache
57 | .rpt2_cache/
58 | .rts2_cache_cjs/
59 | .rts2_cache_es/
60 | .rts2_cache_umd/
61 |
62 | # Optional REPL history
63 | .node_repl_history
64 |
65 | # Output of 'npm pack'
66 | *.tgz
67 |
68 | # Yarn Integrity file
69 | .yarn-integrity
70 |
71 | # dotenv environment variables file
72 | .env
73 | .env.test
74 |
75 | # parcel-bundler cache (https://parceljs.org/)
76 | .cache
77 |
78 | # Next.js build output
79 | .next
80 |
81 | # Nuxt.js build / generate output
82 | .nuxt
83 | dist
84 |
85 | # Gatsby files
86 | .cache/
87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js
88 | # https://nextjs.org/blog/next-9-1#public-directory-support
89 | # public
90 |
91 | # vuepress build output
92 | .vuepress/dist
93 |
94 | # Serverless directories
95 | .serverless/
96 |
97 | # FuseBox cache
98 | .fusebox/
99 |
100 | # DynamoDB Local files
101 | .dynamodb/
102 |
103 | # TernJS port file
104 | .tern-port
105 | /framework-comparisons/test-results/
106 | /framework-comparisons/playwright-report/
107 | /framework-comparisons/playwright/.cache/
108 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | v18.12.0
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) OpenJS Foundation
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | 'Software'), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/enhance/.arc:
--------------------------------------------------------------------------------
1 | @app
2 | myproj
3 |
4 | @plugins
5 | enhance/arc-plugin-enhance
6 |
--------------------------------------------------------------------------------
/enhance/.gitignore:
--------------------------------------------------------------------------------
1 | .env
2 | .DS_Store
3 | node_modules
4 | public/static.json
5 | public/bundles
6 | sam.json
7 | sam.yaml
8 | package-lock.json
9 |
--------------------------------------------------------------------------------
/enhance/app/elements/my-header.mjs:
--------------------------------------------------------------------------------
1 | export default function MyHeader ({ html }) {
2 | return html`
3 |
4 |
5 |

6 |
7 |
8 |

9 |
10 |
11 | `
12 | }
--------------------------------------------------------------------------------
/enhance/app/elements/my-header.test.js:
--------------------------------------------------------------------------------
1 | import { expect, browser } from '@wdio/globals'
2 | import enhance from '@enhance/ssr'
3 |
4 | import MyHeader from './my-header.mjs'
5 |
6 | describe('Enhance Framework', () => {
7 | it('should render MyHeader element correctly', async () => {
8 | const html = enhance({
9 | elements: {
10 | 'my-header': MyHeader
11 | }
12 | })
13 | const actual = document.createElement('div')
14 | actual.innerHTML = (html``).replace(/<(\/*)(html|head|body)>/g, '')
15 | document.body.appendChild(actual)
16 | expect(await browser.$$('img').length).toBe(2)
17 | })
18 | })
19 |
20 |
--------------------------------------------------------------------------------
/enhance/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "scripts": {
3 | "start": "sandbox",
4 | "wdio": "wdio run ./wdio.conf.js"
5 | },
6 | "devDependencies": {
7 | "@architect/sandbox": "latest",
8 | "@enhance/ssr": "^4.0.3",
9 | "@wdio/browser-runner": "^9.7.3",
10 | "@wdio/cli": "^9.7.3",
11 | "@wdio/mocha-framework": "^9.7.3",
12 | "@wdio/spec-reporter": "^9.6.3"
13 | },
14 | "dependencies": {
15 | "@enhance/arc-plugin-enhance": "latest"
16 | },
17 | "type": "module",
18 | "eslintConfig": {
19 | "env": {
20 | "node": true,
21 | "mocha": true,
22 | "browser": true
23 | },
24 | "extends": "eslint:recommended",
25 | "rules": {
26 | "indent": [
27 | "error",
28 | 2
29 | ]
30 | },
31 | "ignorePatterns": [],
32 | "parserOptions": {
33 | "sourceType": "module",
34 | "ecmaVersion": 2022
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/enhance/prefs.arc:
--------------------------------------------------------------------------------
1 | @sandbox
2 | livereload true
3 |
--------------------------------------------------------------------------------
/framework-comparisons/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 | cypress/screenshots
26 | cypress/videos
27 |
--------------------------------------------------------------------------------
/framework-comparisons/cypress.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'cypress'
2 |
3 | export default defineConfig({
4 | component: {
5 | devServer: {
6 | framework: 'react',
7 | bundler: 'vite'
8 | }
9 | }
10 | })
11 |
--------------------------------------------------------------------------------
/framework-comparisons/cypress/support/component-index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Components App
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/framework-comparisons/cypress/support/component.ts:
--------------------------------------------------------------------------------
1 | import { mount } from 'cypress/react18'
2 | import 'cypress-real-events';
3 |
4 | // Ensure global app styles are loaded:
5 | import '../../src/index.css';
6 |
7 | // Augment the Cypress namespace to include type definitions for
8 | // your custom command.
9 | // Alternatively, can be defined in cypress/support/component.d.ts
10 | // with a at the top of your spec.
11 | declare global {
12 | namespace Cypress {
13 | interface Chainable {
14 | mount: typeof mount
15 | }
16 | }
17 | }
18 |
19 | Cypress.Commands.add('mount', mount)
20 |
--------------------------------------------------------------------------------
/framework-comparisons/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React + TS
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/framework-comparisons/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "jsdom-shortcomings",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "tsc && vite build",
9 | "cypress:run": "cypress run --component",
10 | "playwright:run": "playwright test --config ./playwright.config.ts",
11 | "preview": "vite preview",
12 | "test": "vitest -c ./vitest.conf.ts",
13 | "test:ui": "vitest -c ./vitest.conf.ts --ui",
14 | "coverage": "vitest run -c ./vitest.conf.ts --coverage",
15 | "wdio": "wdio run ./wdio.conf.ts"
16 | },
17 | "dependencies": {
18 | "algoliasearch": "^4.19.1",
19 | "react": "^18.2.0",
20 | "react-dom": "^18.2.0",
21 | "react-instantsearch-dom": "^6.40.4"
22 | },
23 | "devDependencies": {
24 | "@playwright/experimental-ct-react": "^1.37.1",
25 | "@playwright/test": "^1.37.1",
26 | "@testing-library/jest-dom": "^6.1.1",
27 | "@testing-library/react": "^14.0.0",
28 | "@types/react": "^18.2.21",
29 | "@types/react-dom": "^18.2.7",
30 | "@types/react-instantsearch-dom": "^6.12.3",
31 | "@vitejs/plugin-react": "^4.0.4",
32 | "@wdio/browser-runner": "^8.15.6",
33 | "@wdio/cli": "^8.15.6",
34 | "@wdio/mocha-framework": "^8.15.6",
35 | "@wdio/spec-reporter": "^8.15.6",
36 | "cypress": "^12.17.4",
37 | "cypress-real-events": "^1.10.0",
38 | "jsdom": "^22.1.0",
39 | "playwright": "^1.37.1",
40 | "react-zoom-pan-pinch": "^3.1.0",
41 | "ts-node": "^10.9.1",
42 | "typescript": "^5.1.6",
43 | "vite": "^4.4.9",
44 | "vitest": "^0.34.2"
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/framework-comparisons/playwright.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from '@playwright/test';
2 | export default defineConfig({
3 | testMatch: /.*\.pw\.tsx/,
4 | use: {
5 | headless: false,
6 | viewport: { width: 1280, height: 720 },
7 | ignoreHTTPSErrors: true,
8 | video: 'on-first-retry',
9 | },
10 | })
11 |
--------------------------------------------------------------------------------
/framework-comparisons/playwright/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/framework-comparisons/playwright/index.ts:
--------------------------------------------------------------------------------
1 | import '../src/index.css'
2 |
--------------------------------------------------------------------------------
/framework-comparisons/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/framework-comparisons/src/App.css:
--------------------------------------------------------------------------------
1 | #root {
2 | max-width: 1280px;
3 | margin: 0 auto;
4 | padding: 2rem;
5 | text-align: center;
6 | }
7 |
8 | .logo {
9 | height: 6em;
10 | padding: 1.5em;
11 | will-change: filter;
12 | transition: filter 300ms;
13 | }
14 | .logo:hover {
15 | filter: drop-shadow(0 0 2em #646cffaa);
16 | }
17 | .logo.react:hover {
18 | filter: drop-shadow(0 0 2em #61dafbaa);
19 | }
20 |
21 | @keyframes logo-spin {
22 | from {
23 | transform: rotate(0deg);
24 | }
25 | to {
26 | transform: rotate(360deg);
27 | }
28 | }
29 |
30 | @media (prefers-reduced-motion: no-preference) {
31 | a:nth-of-type(2) .logo {
32 | animation: logo-spin infinite 20s linear;
33 | }
34 | }
35 |
36 | .testbed {
37 | padding: 2em;
38 | position: relative;
39 | overflow: hidden;
40 | }
41 |
42 | .read-the-docs {
43 | color: #888;
44 | }
45 |
--------------------------------------------------------------------------------
/framework-comparisons/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { Card, SideEffect } from "./components/Card"
2 | import reactLogo from './assets/react.svg'
3 |
4 | import './App.css'
5 |
6 | function App() {
7 | return (
8 |
9 |
17 |
Vite + React
18 |
19 |
20 | Click on the Vite and React logos to learn more
21 |
22 |
23 | )
24 | }
25 |
26 | export default App
27 |
--------------------------------------------------------------------------------
/framework-comparisons/src/components/Card.cy.tsx:
--------------------------------------------------------------------------------
1 | import { Card, SideEffect } from './Card';
2 |
3 | describe('Card Component', () => {
4 | it('can be clicked without a side effect', () => {
5 | cy.mount()
6 | cy.contains('count is 0')
7 | cy.get('button').click()
8 | cy.contains('count is 1')
9 | })
10 |
11 | it('fails because element is not visible', () => {
12 | cy.mount()
13 | cy.contains('count is 0')
14 | /**
15 | * fails due to:
16 | * `
17 | * This element `