Skip to content

Commit c02d8fc

Browse files
Add interlinearizer feature with XML parsing and web view integration (#6)
Add interlinear XML parser, minimal WebView integration, and full test coverage - Parse PT9 interlinear XML (`fast-xml-parser`) into typed `InterlinearData` - Public API in `interlinearizer.d.ts` (`StringRange`, `ClusterData`, `VerseData`, etc.) - Register interlinearizer WebView provider and open React WebView on activation - Document PT9 schema and parser behavior in `src/parsers/pt9-xml.md` - Jest unit tests with 100% coverage on parsers, `main`, and web-view - GitHub Actions workflow for test and lint on push/PR to main - Handle `Excluded` flag and numeric/hash validation in parser - Add test-data sample XML and PAPI mocks for tests
1 parent c49c980 commit c02d8fc

35 files changed

Lines changed: 10482 additions & 17776 deletions

.eslintignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ dist
2929
release
3030
temp-build
3131

32+
# Jest
33+
__mocks__
34+
coverage
35+
3236
# generated files
3337
package-lock.json
3438

.eslintrc.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
// #region shared with https://github.com/paranext/paranext-multi-extension-template/blob/main/.eslintrc.cjs
22

3+
const path = require('path');
4+
35
module.exports = {
46
extends: [
57
// https://github.com/electron-react-boilerplate/eslint-config-erb/blob/main/index.js
@@ -155,6 +157,14 @@ module.exports = {
155157
'import/no-self-import': 'off',
156158
},
157159
},
160+
{
161+
// Jest globals (describe, it, expect, etc.) so ESLint does not report no-undef
162+
files: ['**/*.test.{ts,tsx}', '**/*.spec.{ts,tsx}', '**/__tests__/**/*.{ts,tsx}'],
163+
plugins: ['jest'],
164+
env: {
165+
'jest/globals': true,
166+
},
167+
},
158168
],
159169
parserOptions: {
160170
ecmaVersion: 2022,
@@ -163,11 +173,13 @@ module.exports = {
163173
tsconfigRootDir: __dirname,
164174
createDefaultProgram: true,
165175
},
166-
plugins: ['@typescript-eslint', 'no-type-assertion', 'no-null'],
176+
plugins: ['@typescript-eslint', 'jest', 'no-type-assertion', 'no-null'],
167177
settings: {
168178
'import/resolver': {
169179
typescript: {
170180
alwaysTryTypes: true,
181+
// Absolute path so path aliases (@main, parsers/*) resolve regardless of CWD or file location
182+
project: path.join(__dirname, 'tsconfig.json'),
171183
},
172184
},
173185
'import/parsers': {

.github/workflows/test.yml

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
name: Test
2+
3+
on:
4+
push:
5+
branches: ['main', 'release-prep', 'hotfix-*']
6+
pull_request:
7+
branches: ['main', 'release-prep', 'hotfix-*']
8+
9+
permissions:
10+
contents: read
11+
12+
jobs:
13+
test:
14+
runs-on: ${{ matrix.os }}
15+
strategy:
16+
matrix:
17+
os: [ubuntu-latest]
18+
steps:
19+
- name: Checkout git repo
20+
uses: actions/checkout@v4
21+
with:
22+
path: extension-repo
23+
24+
- name: Checkout paranext-core repo to use its sub-packages
25+
uses: actions/checkout@v4
26+
with:
27+
path: paranext-core
28+
repository: paranext/paranext-core
29+
30+
- name: Setup Node.js
31+
uses: actions/setup-node@v4
32+
with:
33+
cache: 'npm'
34+
cache-dependency-path: |
35+
extension-repo/package-lock.json
36+
paranext-core/package-lock.json
37+
node-version-file: extension-repo/package.json
38+
39+
- name: Install extension dependencies
40+
working-directory: extension-repo
41+
run: npm ci
42+
43+
- name: Install core dependencies
44+
working-directory: paranext-core
45+
run: npm ci --ignore-scripts
46+
47+
- name: Run tests
48+
working-directory: extension-repo
49+
run: npm test

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ dist
1414
release
1515
dist-ssr
1616
*.local
17+
coverage
1718

1819
# formatting and linting
1920
.eslintcache

.prettierignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ dist
2929
release
3030
temp-build
3131

32+
# Jest
33+
__mocks__
34+
coverage
35+
3236
# generated files
3337
package-lock.json
3438

.stylelintignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ dist
2929
release
3030
temp-build
3131

32+
# Jest
33+
__mocks__
34+
coverage
35+
3236
# generated files
3337
package-lock.json
3438

.vscode/extensions.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"dbaeumer.vscode-eslint",
44
"editorconfig.editorconfig",
55
"esbenp.prettier-vscode",
6+
"orta.vscode-jest",
67
"streetsidesoftware.code-spell-checker",
78
"stylelint.vscode-stylelint"
89
]

README.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,21 +90,26 @@ Note: if you [update this extension from the template](#to-update-this-extension
9090

9191
The general file structure for an extension is as follows:
9292

93-
- `package.json` contains information about this extension's npm package. It is required for Platform.Bible to use the extension properly. It is copied into the build folder
93+
- `package.json` (and `package-lock.json`) contain information about this extension's npm package and lockfile. They are required for Platform.Bible to use the extension properly. The lockfile is project-specific and is not synced from the template. The built extension is copied into the build folder
9494
- `manifest.json` is the manifest file that defines the extension and important properties for Platform.Bible. It is copied into the build folder
9595
- `src/` contains the source code for the extension
96-
- `src/main.ts` is the main entry file for the extension
96+
- `src/main.ts` is the main entry file for the extension (registers commands and wires interlinear XML)
9797
- `src/types/interlinearizer.d.ts` is this extension's types file that defines how other extensions can use this extension through the `papi`. It is copied into the build folder
98+
- `src/parsers/interlinearXmlParser.ts` parses interlinear XML into structured data (uses fast-xml-parser). The PT9 XML schema and parsed output are documented in `src/parsers/pt9-xml.md`
9899
- `*.web-view.tsx` files will be treated as React WebViews
100+
- `*.web-view.scss` files provide styles for WebViews
99101
- `*.web-view.html` files are a conventional way to provide HTML WebViews (no special functionality)
102+
- `src/__tests__/` contains unit tests (Jest) for the extension, including parser tests (valid and invalid XML, edge cases) and web-view tests
103+
- `__mocks__/` contains Jest mocks for the PAPI, file modules, and test fixtures used by tests in `src/__tests__/`. The `@papi/backend` and `@papi/frontend` mocks are used mutually exclusively (backend for main.ts tests, frontend for WebView tests); each mock file ends with `export {}` so TypeScript treats it as a module.
100104
- `assets/` contains asset files the extension and its WebViews can retrieve using the `papi-extension:` protocol, as well as textual descriptions in various languages. It is copied into the build folder
101105
- `assets/displayData.json` contains (optionally) a path to the extension's icon file as well as text for the extension's display name, short summary, and path to the full description file
102106
- `assets/descriptions/` contains textual descriptions of the extension in various languages
103107
- `assets/descriptions/description-<locale>.md` contains a brief description of the extension in the language specified by `<locale>`
104108
- `contributions/` contains JSON files the platform uses to extend data structures for things like menus and settings. The JSON files are referenced from the manifest
105109
- `public/` contains other static files that are copied into the build folder
110+
- `test-data/` contains sample interlinear XML (e.g. `Interlinear_en_MAT.xml`) for development and tests
106111
- `.github/` contains files to facilitate integration with GitHub
107-
- `.github/workflows` contains [GitHub Actions](https://github.com/features/actions) workflows for automating various processes in this repo
112+
- `.github/workflows` contains [GitHub Actions](https://github.com/features/actions) workflows for automating various processes in this repo (e.g. **Test** and **Lint** on push/PR to main, release-prep, hotfix-\*; **Publish** and **Bump Versions** manual dispatch; **CodeQL** for security)
108113
- `.github/assets/release-body.md` combined with a generated changelog becomes the body of [releases published using GitHub Actions](#publishing)
109114
- `dist/` is a generated folder containing the built extension files
110115
- `release/` is a generated folder containing a zip of the built extension files
@@ -155,7 +160,6 @@ These steps will walk you through releasing a version on GitHub and bumping the
155160
1. Make sure the versions in this repo are on the version number you want to release. If they are not, manually dispatch the [Bump Versions workflow](#bumping-version-without-publishing-a-release) or run the `bump-versions` npm script to set the versions to what you want to release on the branch you want to release from.
156161

157162
2. Manually dispatch the Publish workflow in GitHub Actions targeting the branch you want to release from. This workflow creates a new pre-release for the version you intend to release and creates a new `bump-versions-<next_version>` branch to bump the version after the release so future changes apply to a new in-progress version instead of to the already released version. This workflow has the following inputs:
158-
159163
- `version`: Enter the version you intend to publish (e.g. 0.2.0). This is simply for verification to make sure you release the code that you intend to release. It is compared to the version in the code, and the workflow will fail if they do not match.
160164
- `newVersionAfterPublishing`: Enter the version you want to bump to after releasing (e.g. 0.3.0-alpha.0). Future changes will apply to this new version instead of to the version that was already released. Leave blank if you don't want to bump.
161165
- `bumpRef`: Enter the Git ref you want to create the bump versions branch from, e.g. `main`. Leave blank if you want to use the branch selected for the workflow run. For example, if you release from a stable branch named `release-prep`, you may want to bump the version on `main` so future development work happens on the new version, then you can rebase `release-prep` onto `main` when you are ready to start preparing the next stable release.

__mocks__/fileMock.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/**
2+
* @file Jest mock for static asset imports (images, fonts, etc.). Importing e.g. `logo.png` in tests will
3+
* receive this string instead of running file loaders. Mirrors webpack's asset/inline and
4+
* asset/resource handling in webpack.config.base.
5+
*
6+
* @see https://jestjs.io/docs/webpack#handling-static-assets
7+
*/
8+
module.exports = 'test-file-stub';

__mocks__/interlinearXmlContent.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/**
2+
* @file Jest mock for webpack ?raw XML import. Exports the contents of test-data/Interlinear_en_MAT.xml
3+
* so interlinearizer.web-view.tsx can parse it in unit tests without webpack.
4+
*/
5+
import fs from 'fs';
6+
import path from 'path';
7+
8+
const xmlPath = path.join(__dirname, '..', 'test-data', 'Interlinear_en_MAT.xml');
9+
module.exports = fs.readFileSync(xmlPath, 'utf-8');

0 commit comments

Comments
 (0)