diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml new file mode 100644 index 0000000..dbb5d10 --- /dev/null +++ b/.github/workflows/checks.yml @@ -0,0 +1,40 @@ +name: Checks + +on: + pull_request: + branches: [ main, master ] + push: + branches: [ main, master ] + +# Least privilege: these jobs only need to read the repository +permissions: + contents: read + +jobs: + checks: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 24 + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Typecheck + run: npm run typecheck + + - name: Lint + run: npm run lint + + - name: Check formatting + run: npm run format:check + + - name: Unit tests + run: npm test + + - name: Build + run: npm run build diff --git a/.github/workflows/cypress.yml b/.github/workflows/cypress.yml index 939153e..2cff272 100644 --- a/.github/workflows/cypress.yml +++ b/.github/workflows/cypress.yml @@ -3,18 +3,15 @@ name: Cypress Tests on: pull_request: branches: [ main, master ] - paths: - - '**.ts' - - '**.js' - - '.github/workflows/cypress.yml' + push: + branches: [ main, master ] -env: - CARGO_TERM_COLOR: always - ENVIRONMENT: development - GH_TOKEN: ${{ github.token }} +# Least privilege: these jobs only need to read the repository +permissions: + contents: read jobs: - build: + e2e: strategy: fail-fast: false matrix: @@ -22,21 +19,44 @@ jobs: runs-on: ${{ matrix.os }} steps: - - name: Clone search and replace - uses: actions/checkout@v3 + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 with: - ref: ${{ github.head_ref }} - repository: forgetso/search-replace - path: search-replace + node-version: 24 + cache: npm - name: Install dependencies - run: | - cd search-replace - ls . - npm install - - - name: Run search and replace tests - run: | - cd search-replace - npm run start 1>server.log 2>&1 & - npm run cypress:run + run: npm ci + + # The specs import from src directly, but the extension is built too so that a failure to + # compile is caught here rather than at release time + - name: Build the extension + run: npm run build + + - name: Serve the test fixtures + run: npm run start > server.log 2>&1 & + + - name: Wait for the fixture server + run: npx wait-on http://localhost:9000/tests/test.html --timeout 60000 + + # wordpress.cy.ts is excluded by cypress.config.ts: it needs the dockerised WordPress from + # `npm run e2e:docker:up`. Run it locally with `npm run test:e2e:wordpress`. + - name: Run end-to-end tests + run: npm run test:e2e + + - name: Upload failure screenshots + if: failure() + uses: actions/upload-artifact@v4 + with: + name: cypress-screenshots + path: src/cypress/screenshots + if-no-files-found: ignore + + - name: Upload server log + if: failure() + uses: actions/upload-artifact@v4 + with: + name: server-log + path: server.log + if-no-files-found: ignore diff --git a/.gitignore b/.gitignore index c32a2d6..eea57ab 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,15 @@ jquery-2.1.4.min.js bootstrap-5.3.0-* dist node_modules -.DS_Store \ No newline at end of file +.DS_Store + +# Build and release artifacts +/releases +*.zip +/search_and_replace_*/ + +# Test output +/coverage +src/cypress/screenshots +src/cypress/videos +server.log diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..4adb3e8 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,24 @@ +# Generated or vendored +node_modules +dist +coverage +releases +package-lock.json +bootstrap-5.3.0-dist +search_and_replace_* +assets/bootstrap* + +# Cypress output +src/cypress/screenshots +src/cypress/videos + +# Large HTML fixtures and hand-maintained extension pages are not prettier-managed +tests +assets/*.html +_locales + +# YAML and markdown are left alone: prettier's 4-space indent fights the 2-space convention +# used by the workflows and docker-compose, for no benefit +*.yml +*.yaml +*.md diff --git a/.prettierrc.js b/.prettierrc.js index 33217ef..9cd247f 100644 --- a/.prettierrc.js +++ b/.prettierrc.js @@ -4,4 +4,7 @@ module.exports = { semi: false, singleQuote: true, printWidth: 120, + // Some files in assets/ are CRLF. Preserving whatever a file already uses keeps prettier + // from rewriting every line of them just to change the line endings. + endOfLine: 'auto', } diff --git a/README.md b/README.md index 3e6cb44..5e3b1c6 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Please Note: 1. You must refresh the page or restart chrome before using. 2. Please select "Input Fields Only" if you are editing text in a text editor or form. -2. The popup will not stay open if you click elsewhere. This is a feature of Chrome. +3. The popup will not stay open if you click elsewhere. This is a feature of Chrome. View a video of it in action here: http://www.youtube.com/watch?v=tf0D8RUdwkI @@ -44,7 +44,51 @@ extension locally. You will now have a local copy of the extension running in your browser. You can edit the TypeScript files and the extension will automatically rebuild thanks to the `npm run watch` command. To see the changes in your browser you will -need to hit the reload button underneath Search and Replace on the [Extensions page](chrome://extensions) and then also -reload the web page on which you are doing the replacements. +need to hit the reload button underneath Search and Replace on the [Extensions page](chrome://extensions). + +### The build + +[Vite](https://vite.dev) builds the extension; `npm run build` produces `dist/`, and `npm run build:dev` +does the same without minification. Each entry point is bundled separately into a self-contained +IIFE, because a manifest v3 content script is injected as a classic script and cannot use ESM +imports. `scripts/build.mjs` drives that, and `vite.config.mts` explains it in more detail. + +Vite transpiles TypeScript with esbuild and does **not** typecheck, so run `npm run typecheck` +(CI does) rather than relying on the build to catch type errors. + +### Tests and checks + +Node 22 or newer is required (jsdom, used by the unit tests, needs a `webidl` API that older +releases lack). + +`npm run checks` runs everything CI runs: typecheck, lint, formatting, unit tests and the build. +The individual scripts are: + +| Command | What it does | +| --- | --- | +| `npm test` | Unit tests ([vitest](https://vitest.dev), jsdom) | +| `npm run test:watch` | Unit tests in watch mode | +| `npm run test:coverage` | Unit tests with a coverage report | +| `npm run typecheck` | `tsc --noEmit` over the extension, the tests and the Cypress specs | +| `npm run lint` / `lint:fix` | eslint | +| `npm run format` / `format:check` | prettier | + +End-to-end tests use [Cypress](https://www.cypress.io) and need the fixture server running: + +```bash +npm run start & # serves the fixtures in ./tests on port 9000 +npm run test:e2e # or `npm run cypress:open` to pick specs interactively +``` + +`wordpress.cy.ts` is excluded from that run because it drives a real WordPress install. To run it: + +```bash +npm run e2e:docker:up +npm run test:e2e:wordpress +npm run e2e:docker:down +``` + +Unit tests live next to the code they cover as `*.test.ts`. They run in jsdom, so anything that +depends on real layout, real iframes or the loaded extension belongs in the Cypress specs instead. Feel free to submit a PR if you make any improvements! diff --git a/assets/icon-128.png b/assets/icon-128.png index 79737f0..52fd347 100644 Binary files a/assets/icon-128.png and b/assets/icon-128.png differ diff --git a/assets/icon-16.png b/assets/icon-16.png index 8b9cd6c..1d93308 100644 Binary files a/assets/icon-16.png and b/assets/icon-16.png differ diff --git a/assets/icon-256.png b/assets/icon-256.png index ed9fae3..bd83ff5 100644 Binary files a/assets/icon-256.png and b/assets/icon-256.png differ diff --git a/assets/icon-32.png b/assets/icon-32.png index adf78e8..fd9e4cf 100644 Binary files a/assets/icon-32.png and b/assets/icon-32.png differ diff --git a/assets/icon-512.png b/assets/icon-512.png index 8736847..11a49aa 100644 Binary files a/assets/icon-512.png and b/assets/icon-512.png differ diff --git a/assets/icon-64.png b/assets/icon-64.png index 084adb1..0771fba 100644 Binary files a/assets/icon-64.png and b/assets/icon-64.png differ diff --git a/assets/icon.png b/assets/icon.png index 8b9cd6c..e50fce2 100644 Binary files a/assets/icon.png and b/assets/icon.png differ diff --git a/assets/logo.svg b/assets/logo.svg index 580bf77..3933b43 100644 --- a/assets/logo.svg +++ b/assets/logo.svg @@ -1,6 +1,186 @@ - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/popup.html b/assets/popup.html index 392c1f4..f98ce58 100644 --- a/assets/popup.html +++ b/assets/popup.html @@ -15,7 +15,7 @@