diff --git a/.babelrc b/.babelrc index 28fec18b0ab..49f6ac45487 100644 --- a/.babelrc +++ b/.babelrc @@ -10,7 +10,7 @@ ] ], "env": { - "test": { + "cypress-coverage": { "plugins": ["istanbul"] } } diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a7068d8576b..741470a4f0f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -64,7 +64,7 @@ jobs: - name: Run gitleaks scan if: always() - uses: gitleaks/gitleaks-action@ff98106e4c7b2bc287b24eaf42907196329070c7 # v2 + uses: gitleaks/gitleaks-action@e0c47f4f8be36e29cdc102c57e68cb5cbf0e8d1e # v3.0.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }} diff --git a/.github/workflows/sonar.yml b/.github/workflows/sonar.yml new file mode 100644 index 00000000000..84a748f612b --- /dev/null +++ b/.github/workflows/sonar.yml @@ -0,0 +1,194 @@ +name: SonarQube + +on: + push: + branches: [main] + pull_request: + branches: [main] + types: [opened, synchronize, reopened] + +permissions: + contents: read + +concurrency: + group: sonar-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + sonarqube-pr: + name: SonarQube PR analysis + if: >- + github.event_name == 'pull_request' && + github.event.pull_request.head.repo.full_name == github.repository && + github.actor != 'dependabot[bot]' + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + with: + fetch-depth: 0 + persist-credentials: false + + # Keep PR feedback close to Automatic Analysis: scan new code on every + # update, but do not install dependencies or run coverage suites. + - name: SonarQube Scan + uses: SonarSource/sonarqube-scan-action@22918119ff8e1ca75a623e15c8296b6ea4fbe28f # v8.2.1 + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + + unit-coverage: + name: Jest coverage + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + with: + persist-credentials: false + + - name: Setup Node.js + uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6 + with: + node-version: "24" + cache: npm + cache-dependency-path: package-lock.json + + - name: Install dependencies + run: npm ci --ignore-scripts + + - name: Run Jest coverage + run: npm run test:unit:coverage + + - name: Verify Jest LCOV + run: test -s coverage/jest/lcov.info + + - name: Upload Jest coverage report + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 + with: + name: jest-coverage-${{ github.run_id }} + path: coverage/jest + if-no-files-found: error + retention-days: 14 + + component-coverage: + name: Cypress component coverage + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + with: + persist-credentials: false + + - name: Setup Node.js + uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6 + with: + node-version: "24" + cache: npm + cache-dependency-path: package-lock.json + + - name: Install dependencies + run: npm ci --ignore-scripts + + - name: Install Cypress binary + run: npx --no-install cypress install + + - name: Run Cypress component coverage + run: npm run test:component:coverage + + - name: Render Cypress component report + run: npm run coverage:component + + - name: Verify Cypress LCOV + run: test -s coverage/component/lcov.info + + - name: Upload Cypress component coverage report + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 + with: + name: component-coverage-${{ github.run_id }} + path: coverage/component + if-no-files-found: error + retention-days: 14 + + mobile-coverage: + name: Mobile Jest coverage + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + with: + persist-credentials: false + + - name: Setup Node.js + uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6 + with: + node-version: "24" + cache: npm + cache-dependency-path: | + package-lock.json + ui/mobile/package-lock.json + + - name: Install root dependencies + run: npm ci --ignore-scripts + + - name: Install mobile dependencies + run: npm --prefix ui/mobile ci --ignore-scripts + + - name: Run mobile Jest coverage + run: npm --prefix ui/mobile run test:coverage -- --ci + + - name: Verify mobile Jest LCOV + run: test -s ui/mobile/coverage/lcov.info + + - name: Upload mobile Jest coverage report + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 + with: + name: mobile-coverage-${{ github.run_id }} + path: ui/mobile/coverage + if-no-files-found: error + retention-days: 14 + + sonarqube-main: + name: SonarQube main analysis + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + needs: [unit-coverage, component-coverage, mobile-coverage] + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + with: + fetch-depth: 0 + persist-credentials: false + + - name: Download Jest coverage report + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7 + with: + name: jest-coverage-${{ github.run_id }} + path: coverage/jest + + - name: Download Cypress component coverage report + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7 + with: + name: component-coverage-${{ github.run_id }} + path: coverage/component + + - name: Download mobile Jest coverage report + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7 + with: + name: mobile-coverage-${{ github.run_id }} + path: ui/mobile/coverage + + - name: Verify imported LCOV reports + run: | + test -s coverage/jest/lcov.info + test -s coverage/component/lcov.info + test -s ui/mobile/coverage/lcov.info + + - name: SonarQube Scan with coverage + uses: SonarSource/sonarqube-scan-action@22918119ff8e1ca75a623e15c8296b6ea4fbe28f # v8.2.1 + with: + args: >- + -Dsonar.javascript.lcov.reportPaths=coverage/jest/lcov.info,coverage/component/lcov.info,ui/mobile/coverage/lcov.info + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} diff --git a/cypress.config.ts b/cypress.config.ts index 0127dd55627..b8220ffbe9e 100644 --- a/cypress.config.ts +++ b/cypress.config.ts @@ -10,8 +10,9 @@ const componentCoverageOptions = { 'coverage/**/*', ], include: [ + 'app/**/*.{js,jsx,ts,tsx}', 'core/**/*.{js,jsx,ts,tsx}', - 'ui/**/*.{js,jsx,ts,tsx}', + 'ui/web/**/*.{js,jsx,ts,tsx}', ], } @@ -55,9 +56,14 @@ export default defineConfig({ const coverageEnabled = config.env.codeCoverage === true || config.env.codeCoverage === 'true' if (coverageEnabled) { + // Cypress is the owner of browser-side instrumentation. Jest adds its + // own Istanbul plugin when --coverage is enabled, so do not use the + // generic test Babel environment for this process. + process.env.BABEL_ENV = 'cypress-coverage' config.env.codeCoverage = componentCoverageOptions require('@cypress/code-coverage/task')(on, config) } else { + process.env.BABEL_ENV = 'test' delete config.env.codeCoverage } diff --git a/docs/ai/deployment/2026-07-21-feature-sonar-test-coverage.md b/docs/ai/deployment/2026-07-21-feature-sonar-test-coverage.md new file mode 100644 index 00000000000..e133d24e3d1 --- /dev/null +++ b/docs/ai/deployment/2026-07-21-feature-sonar-test-coverage.md @@ -0,0 +1,70 @@ +--- +phase: deployment +title: Sonar Test Coverage Deployment +description: One-time migration from Automatic Analysis to GitHub Actions +--- + +# Sonar Test Coverage Deployment + +## Infrastructure + +- GitHub Actions runs the scanner and coverage producers. +- SonarQube Cloud stores static-analysis and main coverage measures. +- GitHub Actions artifacts store independent root Jest, Cypress, and mobile Jest + reports for 14 days. + +## Deployment Pipeline + +### Build Process + +- PR: checkout full Git history and run only the Sonar scanner. +- Main: run root Jest, Cypress, and mobile Jest coverage in parallel, transfer + all reports to a final job, then run the scanner. + +### CI/CD Pipeline + +The dedicated `.github/workflows/sonar.yml` does not replace the existing +application `.github/workflows/build.yml`. + +## Environment Configuration + +### Development + +Local SonarQube keeps project key `EverFreeNote` through a scanner command-line +override. Local reports must be generated before a local scan. + +### Production + +- SonarQube Cloud project: `koreyba_EverFreeNote`. +- Organization: `koreyba`. +- GitHub secret: `SONAR_TOKEN`. + +## Deployment Steps + +1. Ensure `SONAR_TOKEN` exists in GitHub repository secrets. +2. Immediately before pushing/enabling this workflow, open SonarQube Cloud + `Administration > Analysis Method` and disable Automatic Analysis. Do not + leave both analysis methods active for the same commit. +3. Push the workflow branch or update its PR and confirm the scanner-only PR + check behaves like the previous automatic check. +4. Merge to `main` and confirm the resulting workflow imports all three LCOV + files before publishing the main analysis. +5. Confirm the analyzed revision in SonarQube Cloud matches the merged commit. +6. Update branch protection if the required-check identity changed from the + SonarQube Cloud GitHub App check to the GitHub Actions job. + +## Database Migrations + +None. + +## Secrets Management + +`SONAR_TOKEN` is stored only in GitHub Secrets. Rotate it in SonarQube Cloud and +replace the GitHub secret if it is exposed or its owner changes. + +## Rollback Plan + +1. Disable the GitHub Actions Sonar workflow. +2. Re-enable Automatic Analysis in SonarQube Cloud. +3. Accept that coverage will no longer be imported while Automatic Analysis is + active. diff --git a/docs/ai/design/feature-sonar-test-coverage.md b/docs/ai/design/feature-sonar-test-coverage.md new file mode 100644 index 00000000000..751e3523b6f --- /dev/null +++ b/docs/ai/design/feature-sonar-test-coverage.md @@ -0,0 +1,130 @@ +--- +phase: design +title: Sonar Test Coverage Design +description: CI architecture for fast PR analysis and deterministic main coverage +--- + +# Sonar Test Coverage Design + +## Architecture Overview + +```mermaid +flowchart TD + PR["PR opened or updated"] --> PRScan["Lightweight Sonar scanner"] + PRScan --> PRCloud["SonarQube Cloud PR / new-code result"] + + Main["Commit merged to main"] --> Unit["Jest unit coverage"] + Main --> Component["Cypress component coverage"] + Main --> Mobile["Mobile Jest coverage"] + Unit --> UnitLCOV["coverage/jest/lcov.info"] + Component --> ComponentLCOV["coverage/component/lcov.info"] + Mobile --> MobileLCOV["ui/mobile/coverage/lcov.info"] + UnitLCOV --> UnitArtifact["Independent Jest artifact"] + ComponentLCOV --> ComponentArtifact["Independent Cypress artifact"] + MobileLCOV --> MobileArtifact["Independent mobile Jest artifact"] + UnitLCOV --> MainScan["Main Sonar scanner"] + ComponentLCOV --> MainScan + MobileLCOV --> MainScan + MainScan --> MainCloud["SonarQube Cloud main baseline"] + + Semgrep["Semgrep static security scan"] --> SemgrepCloud["Semgrep findings"] +``` + +GitHub Actions replaces SonarQube Cloud Automatic Analysis. The PR job invokes +only the scanner. The main workflow runs three independent coverage producers +in parallel, downloads their immutable artifacts into a final scanner job, and +passes all LCOV paths explicitly. + +## Data Models + +- `coverage/jest/lcov.info`: unit-test execution map for `app`, `core`, and + `ui/web`. +- `coverage/component/lcov.info`: component-test execution map for the same + production-code scope. +- `ui/mobile/coverage/lcov.info`: Jest execution map for the Expo/mobile source + and any shared core code exercised by mobile tests. +- `SONAR_TOKEN`: GitHub secret used only by scanner jobs. +- Sonar analysis parameters: cloud host, organization, cloud project key, and + optional LCOV paths for the main analysis. + +## API Design + +The Sonar scanner uploads analysis data to `https://sonarcloud.io`. GitHub +Actions provides PR metadata automatically for `pull_request` events. Main +analysis is identified by the checked-out `main` revision. + +Coverage producers do not call Sonar directly. The final job is the sole owner +of a main-revision analysis, preventing duplicate or partial uploads. + +## Component Breakdown + +- `sonar-project.properties` owns cloud identity and stable source/test scope. +- `.github/workflows/sonar.yml` owns event routing, report production, artifact + transfer, and cloud-specific scanner parameters. +- Jest owns instrumentation for unit coverage and writes `coverage/jest`. +- Babel Istanbul plus `@cypress/code-coverage` own browser instrumentation; + NYC renders the final independent component report. +- Mobile Jest owns React Native/Expo test instrumentation and writes + `ui/mobile/coverage`. +- Semgrep remains a separate SAST workflow. Its platform does not import LCOV + or represent runtime test coverage. + +## Design Decisions + +### PR analysis remains fast + +PR scans run on `opened`, `synchronize`, and `reopened`, matching the effective +Automatic Analysis cadence. They do not install dependencies or run tests. +Coverage is therefore unavailable on ordinary PR analyses by design. + +### Main coverage is deterministic + +The scanner never relies on implicit discovery of `coverage/lcov.info`. +Producer jobs must create and upload their named LCOV file; the consumer fails +if any report is absent. + +### Reports remain independent + +Root Jest, Cypress, and mobile Jest retain separate reports, percentages, HTML +output, and artifacts. SonarQube Cloud exposes one coverage measure per project, +so its main metric represents code covered by any configured test layer. +Overlapping lines, including shared core code, are deduplicated and the result +cannot exceed 100 percent. Sonar Measures still allows drill-down by directory, +but not by test runner. + +### Cloud identity is the repository default + +`sonar-project.properties` uses the identifiers generated by SonarQube Cloud: +project `koreyba_EverFreeNote` and organization `koreyba`. A local scanner must +override `sonar.projectKey=EverFreeNote` and the local host URL. This makes the +committed default match the shared CI service while retaining local diagnostics. + +### One product uses multiple TypeScript programs + +The repository is one Sonar project because it has one shared PR and Quality +Gate. The scanner uses `tsconfig.json`, `tsconfig.tests.json`, and the +dependency-free `ui/mobile/tsconfig.sonar.json`. The custom mobile analysis +config copies the essential Expo compiler options because PR scanner jobs +intentionally do not install `node_modules`. Root application (`app` and +`ui/web`), shared `core`, and `ui/mobile` are production coverage scope. +Infrastructure and tooling such as Supabase functions and scripts remain +statically analyzed but are excluded from this test-coverage metric until they +have an owned coverage producer. + +## Non-Functional Requirements + +- All three coverage producers run in parallel so main latency is approximately + the slowest producer plus scanner time, not the sum of all suites. +- Actions and scanner dependencies are pinned to immutable revisions. +- Untrusted fork code never receives `SONAR_TOKEN`. +- Artifacts are retained for 14 days for diagnosis without becoming permanent + storage. +- Automatic Analysis must be disabled before the workflow is activated to + prevent duplicate-analysis rejection. + +## References + +- [SonarQube Cloud Automatic Analysis](https://docs.sonarsource.com/sonarqube-cloud/advanced-setup/automatic-analysis) +- [JavaScript and TypeScript test coverage](https://docs.sonarsource.com/sonarqube-cloud/enriching/test-coverage/javascript-typescript-test-coverage) +- [JavaScript and TypeScript analysis and TSConfig guidance](https://docs.sonarsource.com/sonarqube-cloud/advanced-setup/languages/javascript-typescript-css) +- [Semgrep CI sample configurations](https://semgrep.dev/docs/semgrep-ci/sample-ci-configs) diff --git a/docs/ai/design/feature-test-coverage-architecture.md b/docs/ai/design/feature-test-coverage-architecture.md new file mode 100644 index 00000000000..fa7547bf767 --- /dev/null +++ b/docs/ai/design/feature-test-coverage-architecture.md @@ -0,0 +1,64 @@ +--- +phase: design +title: Test Coverage Architecture +description: Independent coverage for root Jest, Cypress, and mobile Jest tests +--- + +# Test Coverage Architecture + +## Decision + +The repository has three independent coverage producers and reporting layers: + +```mermaid +flowchart LR + U["Jest unit and integration tests"] --> J["Jest Istanbul map"] + C["Cypress component tests"] --> K["NYC Istanbul map"] + M["Mobile Jest tests"] --> MJ["Mobile Jest Istanbul map"] + J --> R1["Jest HTML, text and LCOV report"] + K --> R2["Cypress/NYC HTML, text and LCOV report"] + MJ --> R3["Mobile Jest HTML, text and LCOV report"] +``` + +Root Jest is authoritative for core and web unit/integration behavior. Cypress +is authoritative for browser-rendered component and interaction behavior. +Mobile Jest is authoritative for React Native/Expo behavior. Their percentages +are intentionally not added or averaged. + +## Instrumentation ownership + +- `babel-jest` instruments code when Jest receives `--coverage`. +- Babel does not add Istanbul for the generic `test` environment. +- Cypress sets `BABEL_ENV=cypress-coverage` only for coverage runs, and the + Babel config adds Istanbul in that environment. +- A single test process therefore has exactly one Istanbul instrumentation + owner. + +## Coverage scope and quality goal + +The measured product scope is root web (`app/**` and `ui/web/**`), shared +`core/**`, and `ui/mobile/**`, excluding declarations and test files. Producer +percentages remain independent. Coverage is initially visible without becoming +a pull-request merge gate; a new-code threshold can be adopted later after the +baseline is stable. + +Sonar consumes all LCOV files to produce a derived project-level view: code +covered by at least one test layer. Overlapping lines are not counted twice, so +this union cannot exceed 100 percent. It does not replace producer reports. + +## Operational commands + +- `npm run test:unit:coverage` writes the Jest report to `coverage/jest`. +- `npm run test:component:coverage` writes Cypress maps to `.nyc_output`. +- `npm run coverage:component` renders the Cypress map to + `coverage/component`. +- `npm --prefix ui/mobile run test:coverage` writes mobile Jest coverage to + `ui/mobile/coverage`. + +Coverage directories are generated artifacts. CI runs on a clean checkout, so +no preparation script is required. If a local Cypress coverage rerun must be +isolated from an interrupted run, remove `.nyc_output` and +`coverage/component` manually before starting it. + +CI publishes all reports separately. Only a successful `main` coverage run +feeds all three files to SonarQube Cloud. diff --git a/docs/ai/implementation/feature-sonar-test-coverage.md b/docs/ai/implementation/feature-sonar-test-coverage.md new file mode 100644 index 00000000000..eb30073e5b4 --- /dev/null +++ b/docs/ai/implementation/feature-sonar-test-coverage.md @@ -0,0 +1,71 @@ +--- +phase: implementation +title: Sonar Test Coverage Implementation +description: Implementation notes for SonarQube Cloud coverage reporting +--- + +# Sonar Test Coverage Implementation + +## Development Setup + +- Node.js 24 and `npm ci` match the existing GitHub Actions workflows. +- A local scan may continue to use project key `EverFreeNote` and + `http://localhost:9000` by passing both values as scanner overrides. +- Cloud deployment requires GitHub secret `SONAR_TOKEN` and disabled SonarQube + Cloud Automatic Analysis. + +## Code Structure + +- `.github/workflows/sonar.yml`: PR and main analysis orchestration. +- `jest.config.cjs`: Jest coverage scope and output directory. +- `ui/mobile/jest.config.js`: mobile coverage scope and output directory. +- `ui/mobile/tsconfig.sonar.json`: dependency-free mobile TypeScript program + for scanner-only jobs. +- `package.json`: independent coverage commands and NYC report configuration. +- `sonar-project.properties`: static-analysis scope and test classification. + +## Implementation Notes + +- Mobile coverage runs in-band to avoid CPU-contention timeouts from Expo/Jest + transforms on small CI runners. +- The mobile LCOV reporter uses the repository root as `projectRoot`, producing + `SF:ui/mobile/...` paths that the root Sonar scan can resolve. +- Root and component coverage include `app`, `core`, and `ui/web`; mobile Jest + owns `ui/mobile` while also recording imported shared-core modules. +- The workflow has five jobs: scanner-only PR analysis, three parallel main + coverage producers, and one main scanner that requires all producers. +- Semgrep was left unchanged because it has no supported runtime LCOV ingestion + path; its existing workflow remains an independent SAST signal. + +## Integration Points + +- SonarQube Cloud project: `koreyba_EverFreeNote`. +- SonarQube Cloud organization: `koreyba`. +- GitHub secret: `SONAR_TOKEN`. +- TypeScript programs: `tsconfig.json`, `tsconfig.tests.json`, and + `ui/mobile/tsconfig.sonar.json`. +- Semgrep remains connected only through `.github/workflows/semgrep.yml`. + +## Error Handling + +- Coverage producers fail if tests fail or their LCOV output is missing/empty. +- CI does not need a cleanup hook because each producer starts on a clean + runner. Local interrupted Cypress runs may be cleaned manually by removing + `.nyc_output` and `coverage/component` before rerunning coverage. +- Artifact download failure prevents the main scanner from publishing partial + coverage. +- Scanner failures remain visible as the SonarCloud code-analysis check. + +## Performance Considerations + +- Coverage producers run concurrently. +- npm caching uses `package-lock.json`. +- PR scanning does not install dependencies or execute tests. + +## Security Notes + +- `SONAR_TOKEN` is read from GitHub Secrets and is never stored in the repo. +- Authenticated PR scans are restricted to branches in the same repository and + exclude Dependabot. +- `pull_request_target` is not used, so untrusted code cannot execute with the + Sonar secret. diff --git a/docs/ai/monitoring/2026-07-21-feature-sonar-test-coverage.md b/docs/ai/monitoring/2026-07-21-feature-sonar-test-coverage.md new file mode 100644 index 00000000000..5c9d3759082 --- /dev/null +++ b/docs/ai/monitoring/2026-07-21-feature-sonar-test-coverage.md @@ -0,0 +1,77 @@ +--- +phase: monitoring +title: Sonar Test Coverage Monitoring +description: Operational checks for main coverage and PR Sonar analysis +--- + +# Sonar Test Coverage Monitoring + +## Key Metrics + +### Performance Metrics + +- Duration of root Jest, Cypress, mobile Jest, and final Sonar jobs. +- GitHub Actions consumption per merged main revision. + +### Business Metrics + +- Main coverage reported by SonarQube Cloud. +- Independent root Jest, Cypress, and mobile Jest percentages from artifacts. +- Coverage on new code is observed after merge; it is not a PR gate. + +### Error Metrics + +- Failed coverage producers. +- Missing or empty LCOV files. +- Failed artifact downloads. +- Sonar scanner authentication or duplicate-analysis errors. + +## Monitoring Tools + +- GitHub Actions job logs and summaries. +- GitHub Actions artifacts. +- SonarQube Cloud project dashboard and activity history. +- Semgrep remains a separate security dashboard. + +## Logging Strategy + +Coverage commands print text summaries. The scanner log must identify all three +explicit LCOV paths on main. Tokens and other secrets must never be printed. + +## Alerts & Notifications + +### Critical Alerts + +- Main Sonar job fails after a merge: inspect failed producer/scanner and rerun + after correction. +- Sonar reports no coverage after a successful workflow: verify LCOV import + lines and analysis revision. + +### Warning Alerts + +- Material drop in main or new-code coverage: inspect independent artifacts to + identify the responsible test layer. +- Significant increase in main workflow duration: inspect Cypress duration and + dependency-cache hits. + +## Dashboards + +- SonarQube Cloud is the aggregate main-code view. +- Root Jest, Cypress, and mobile Jest HTML artifacts are the layer-specific + diagnostic views. + +## Incident Response + +1. Confirm the workflow analyzed the expected commit. +2. Inspect all producer jobs and verify non-empty LCOV artifacts. +3. Inspect scanner logs for all imported paths. +4. Re-run the failed workflow when the failure is transient. +5. Roll back to Automatic Analysis only if static PR analysis must be restored + urgently; coverage will be unavailable in that mode. + +## Health Checks + +- Every main analysis date matches a merged main commit. +- All three artifacts exist for every successful main Sonar run. +- A representative PR update receives a Sonar new-code result without coverage + jobs. diff --git a/docs/ai/planning/feature-sonar-test-coverage.md b/docs/ai/planning/feature-sonar-test-coverage.md new file mode 100644 index 00000000000..65d7e6f3a81 --- /dev/null +++ b/docs/ai/planning/feature-sonar-test-coverage.md @@ -0,0 +1,85 @@ +--- +phase: planning +title: Sonar Test Coverage Plan +description: Delivery plan for deterministic main coverage in SonarQube Cloud +--- + +# Sonar Test Coverage Plan + +## Milestones + +- [x] Milestone 1: Coverage producers emit independent deterministic LCOV files. +- [x] Milestone 2: GitHub Actions preserves fast PR analysis and is configured + to publish main coverage. +- [ ] Milestone 3: Documentation, validation, and rollout guidance are complete. + +## Task Breakdown + +### Phase 1: Foundation + +- [x] Task 1.1: Record event cadence, report semantics, and non-goals. +- [x] Task 1.2: Align Sonar source, test, and coverage scopes. +- [x] Task 1.3: Ensure component reporting emits + `coverage/component/lcov.info` independently from Jest. +- [x] Task 1.4: Configure mobile Jest to emit an independent complete LCOV + report under `ui/mobile/coverage`. + +### Phase 2: Core Features + +- [x] Task 2.1: Add a lightweight PR Sonar job without coverage commands. +- [x] Task 2.2: Add parallel root Jest, Cypress, and mobile Jest coverage jobs + for pushes to `main`. +- [x] Task 2.3: Upload each report separately and make the main scanner depend on + all three successful producers. +- [x] Task 2.4: Pass cloud project identity and all three LCOV paths explicitly. +- [x] Task 2.5: Analyze root, tests, and mobile with their respective TypeScript + configurations in one Sonar project. + +### Phase 3: Integration & Polish + +- [x] Task 3.1: Document the SonarCloud Automatic Analysis migration sequence + and required secret. +- [x] Task 3.2: Record the Semgrep capability decision. +- [ ] Task 3.3: Validate YAML, DevKit docs, lint, report generation, and scanner + configuration without publishing an unrequested cloud analysis. +- [x] Task 3.4: Perform implementation conformance and code review. + +The implementation validation is otherwise complete, with the Cypress +limitation recorded in the testing document. DevKit recognized all seven +feature documents, but its required branch-name check is still failed because +the working branch is intentionally retained as `fix-coverage-issue` instead +of `feature-sonar-test-coverage`. This is an accepted exception for the current +checkout; Phase 3 remains incomplete until the feature lint is rerun on the +expected branch or the exception is formally retired. + +## Dependencies + +- Tasks 1.1-1.4 precede workflow implementation. +- All three coverage jobs must succeed before the main scanner job. +- SonarQube Cloud Automatic Analysis must be disabled and `SONAR_TOKEN` must be + configured before the workflow is enabled remotely. + +## Timeline & Estimates + +- Configuration and documentation: one implementation session. +- Local verification: dominated by the full Jest, Cypress, and mobile coverage + suites. +- Deployment verification: first merged `main` workflow after SonarCloud setup. + +## Risks & Mitigation + +- **Duplicate analysis:** disable Automatic Analysis before CI-based scans. +- **Stale LCOV:** use clean jobs, explicit artifact names, and existence checks. +- **Slow main feedback:** run producers in parallel and cache npm dependencies. +- **Fork secret exposure:** skip authenticated Sonar jobs for untrusted PR heads. +- **Misleading aggregate:** keep independent artifacts and document Sonar as a + union metric. +- **Coverage regression after merge:** accept this intentionally; PR coverage + enforcement is outside the approved operating model. + +## Resources Needed + +- SonarQube Cloud project administrator for the one-time analysis-method switch. +- GitHub repository administrator for the `SONAR_TOKEN` secret and, if needed, + required-check migration. +- GitHub-hosted Ubuntu runners. diff --git a/docs/ai/requirements/feature-sonar-test-coverage.md b/docs/ai/requirements/feature-sonar-test-coverage.md new file mode 100644 index 00000000000..891a4368333 --- /dev/null +++ b/docs/ai/requirements/feature-sonar-test-coverage.md @@ -0,0 +1,92 @@ +--- +phase: requirements +title: Sonar Test Coverage Requirements +description: Make web, core, component, and mobile coverage deterministic and visible in SonarQube Cloud +--- + +# Sonar Test Coverage Requirements + +## Problem Statement + +Test coverage is currently visible only accidentally in the local SonarQube +instance. The npm Sonar scanner can discover a previously generated +`coverage/lcov.info`, so a local scan can publish stale or ambiguously sourced +data if generated artifacts are left in the workspace. +SonarQube Cloud uses Automatic Analysis, which cannot import LCOV reports. + +Maintainers need a deterministic main-branch coverage baseline without adding +the cost of root Jest, Cypress, and mobile Jest coverage runs to every +pull-request update. + +## Goals & Objectives + +- Preserve automatic Sonar analysis of new code on every pull-request push, + without running coverage tests in that path. +- After a merge to `main`, produce fresh root Jest, Cypress component, and + mobile Jest LCOV reports before running the Sonar scanner. +- Keep all three reports independent and downloadable as separate CI artifacts. +- Import all reports into one main-branch Sonar analysis. Sonar's single + project-level coverage measure is a derived union, not a sum or average of + the producer percentages. +- Analyze the repository as one EverFreeNote product with multiple TypeScript + configurations: root application/core, root tests, and mobile. +- Document why Semgrep remains independent from runtime test coverage. + +### Non-goals + +- Running coverage on every pull request. +- Making coverage a required pull-request quality gate. +- Adding scheduled or nightly coverage runs. +- Adding browser E2E or mobile E2E coverage to this feature. +- Creating separate SonarQube Cloud projects for each test layer. + +## User Stories & Use Cases + +- As a maintainer, I want fast Sonar feedback after every PR push so that the + existing new-code review behavior remains available. +- As a maintainer, I want every merged `main` revision to publish fresh coverage + so that the Sonar dashboard reflects the repository baseline. +- As a developer, I want separate root Jest, Cypress, and mobile Jest artifacts + so that I can diagnose gaps in the appropriate product area and test layer. +- As a maintainer, I want a failed or missing coverage producer to prevent the + main Sonar scan so that stale coverage is never uploaded. + +## Success Criteria + +- An eligible trusted PR event targeting `main`—opened, synchronized, or + reopened from the same repository and not Dependabot—runs one SonarQube Cloud + project scan without invoking any coverage command. +- A push to `main` runs root Jest, Cypress, and mobile Jest coverage in parallel + and runs Sonar only after all three jobs succeed. +- Main-branch analysis imports `coverage/jest/lcov.info` and + `coverage/component/lcov.info` plus `ui/mobile/coverage/lcov.info` explicitly. +- Each coverage directory is uploaded as a separate artifact with a finite + retention period. +- A missing LCOV file fails its producer job. +- The workflow uses the existing public SonarQube Cloud project + `koreyba_EverFreeNote` in organization `koreyba`. +- Semgrep configuration is not presented as a test-coverage integration. + +## Constraints & Assumptions + +- SonarQube Cloud Automatic Analysis and CI-based analysis cannot coexist for + the same project. Automatic Analysis must be disabled before enabling the new + workflow. +- GitHub must contain a `SONAR_TOKEN` repository secret. +- PR scans from forks and Dependabot cannot safely receive the repository + secret and may therefore be skipped. +- The repository remains on a SonarQube Cloud plan that supports its existing + public-project PR analysis. +- One Sonar project owns one PR Quality Gate. Multiple TSConfig files do not + create separate Sonar projects. +- CI work is performed on clean GitHub-hosted runners, but report paths are + still explicit to prevent accidental stale-report discovery. +- Local generated coverage artifacts are not managed by the test command; a + developer removes them manually when an interrupted rerun must be isolated. + +## Questions & Open Items + +- After deployment, measure the duration and Actions usage of the main coverage + workflow before considering further optimization. +- A coverage Quality Gate may be introduced later for main/new code after the + baseline is stable; it is not part of this feature. diff --git a/docs/ai/testing/feature-sonar-reliability.md b/docs/ai/testing/feature-sonar-reliability.md index c97d998aae4..aefe26d9f48 100644 --- a/docs/ai/testing/feature-sonar-reliability.md +++ b/docs/ai/testing/feature-sonar-reliability.md @@ -30,7 +30,8 @@ description: Regression coverage and validation results for the July 2026 reliab ## Integration Tests -- [x] The complete core and web unit suites pass together: 53 suites, 487 tests. +- [x] The complete root Jest coverage suite passes with `unit-core`, + `integration-core`, and `unit-web`: 55 suites and 508 tests. - [x] Targeted Cypress component tests pass for `NoteSearchItem`: 4 tests. - [x] Targeted Cypress component tests pass for `WordPressSettingsDialog`: 5 tests. @@ -51,10 +52,14 @@ description: Regression coverage and validation results for the July 2026 reliab - `npm run type-check`: passed. - `npm run type-check:tests`: passed. - `npx eslint . --max-warnings=0`: passed. -- `npm run test:unit -- --runInBand`: passed, 53 suites and 487 tests. +- `npm run test:unit -- --runInBand`: passed, 53 suites and 488 tests. - Targeted Cypress component run: passed, 9 tests across the two affected specs. +- Root Jest coverage run: passed, 55 suites and 508 tests across the three + selected projects. - `git diff --check`: passed. -- Jest coverage collection is currently blocked by duplicate Istanbul instrumentation: `.babelrc` loads `babel-plugin-istanbul` for `test`, while Jest's Babel coverage provider injects the same plugin when `--coverage` is enabled. This is an existing test-infrastructure issue; normal test execution is unaffected. +- Jest coverage uses `npm run test:unit:coverage` and writes an independent report to `coverage/jest`. +- Cypress component coverage uses `npm run test:component:coverage` followed by `npm run coverage:component` and writes an independent report to `coverage/component`. +- Babel instrumentation is owned by the runner: Jest injects Istanbul for `--coverage`, while Cypress uses the dedicated `cypress-coverage` Babel environment. These two root-project percentages are intentionally reported separately; mobile Jest is the third independent producer in the Sonar coverage architecture. ## Manual Testing diff --git a/docs/ai/testing/feature-sonar-test-coverage.md b/docs/ai/testing/feature-sonar-test-coverage.md new file mode 100644 index 00000000000..5cc3fb40c5d --- /dev/null +++ b/docs/ai/testing/feature-sonar-test-coverage.md @@ -0,0 +1,100 @@ +--- +phase: testing +title: Sonar Test Coverage Testing Strategy +description: Validation strategy for deterministic coverage production and import +--- + +# Sonar Test Coverage Testing Strategy + +## Test Coverage Goals + +This is CI infrastructure. Validation targets report correctness, event routing, +failure behavior, and compatibility with existing tests rather than adding +application test cases. + +## Unit Tests + +- [x] Full Jest coverage command succeeds with root unit and integration + projects: 55 suites and 508 tests. +- [x] Jest emits a non-empty `coverage/jest/lcov.info` covering `app`, `core`, + and `ui/web`. +- [x] Existing unit test suites remain green. +- [x] Full mobile Jest coverage command succeeds in-band: 44 suites and 390 + tests. +- [x] Mobile Jest emits a non-empty `ui/mobile/coverage/lcov.info` with + repository-relative `SF:ui/mobile/...` paths. + +## Integration Tests + +- [x] Cypress coverage instrumentation runs in its dedicated Babel environment; + an earlier focused coverage run passed four tests. +- [x] NYC emits its independent component report under `coverage/component`. +- [x] Root Jest, Cypress, and mobile Jest outputs use separate directories. +- [x] Sonar analysis receives all three explicit paths only in the main job. +- [x] The dependency-free mobile Sonar TSConfig parses successfully. +- [x] PR workflow contains no coverage test command. + +The full Cypress coverage suite was not completed locally: an earlier full run +was intentionally interrupted, and a later cold focused webpack build exceeded +the five-minute local command window. The main workflow intentionally has no +such application-level timeout and remains the authoritative full-suite check. + +## End-to-End Tests + +- [ ] First merged workflow publishes main coverage to SonarQube Cloud. +- [ ] A later PR push produces a Sonar new-code result without running coverage. +- [ ] SonarQube Cloud PR decoration/check naming is compatible with branch + protection. + +These deployment checks require repository secrets and SonarQube Cloud state and +cannot be completed solely in the local checkout. + +## Test Data + +- Existing root Jest, Cypress component, and mobile Jest tests are the coverage + input. +- No production data or external test accounts are required. + +## Test Reporting & Coverage + +- Jest command: `npm run test:unit:coverage`. +- Cypress commands: `npm run test:component:coverage` followed by + `npm run coverage:component`. +- Mobile command: `npm --prefix ui/mobile run test:coverage`. +- CI artifacts: root Jest, Cypress component, and mobile Jest reports retained + independently for 14 days. +- Sonar main coverage: derived union of all three LCOV files. + +## Manual Testing + +- Disable Automatic Analysis in SonarQube Cloud. +- Add `SONAR_TOKEN` to GitHub repository secrets. +- Confirm the first main run reports all three LCOV files in scanner logs. +- Confirm the Sonar dashboard updates coverage for the analyzed main revision. + +## Performance Testing + +- Record total main workflow duration and individual producer durations after + the first deployment run. +- No nightly benchmark is required. + +## Local Validation Results + +- `npm run type-check`: passed. +- `npm run type-check:tests`: passed. +- `npm --prefix ui/mobile run type-check`: passed. +- `npx eslint . --max-warnings=0`: passed. +- Sonar workflow YAML and five-job dependency structure: passed. +- `npx tsc -p ui/mobile/tsconfig.sonar.json --noEmit`: passed. +- `git diff --check`: passed apart from Git's informational LF/CRLF warnings. + +The previous root coverage run discovered 53 suites and 488 tests while the +coverage command selected only `unit-core` and `unit-web`. The current command +also selects `integration-core`, intentionally adding its 2 suites and 20 +tests; all 55 suites and 508 tests pass. This count is synchronized with +`feature-sonar-reliability.md`. + +## Bug Tracking + +- Missing or stale reports are release-blocking for this workflow. +- A scanner-only PR regression is treated as a behavior-preservation defect. diff --git a/jest.config.cjs b/jest.config.cjs index 4b25de0f374..5c0742d50c5 100644 --- a/jest.config.cjs +++ b/jest.config.cjs @@ -48,6 +48,15 @@ const coreIntegrationAllureOptions = { } module.exports = { + collectCoverageFrom: [ + 'app/**/*.{js,jsx,ts,tsx}', + 'core/**/*.{js,jsx,ts,tsx}', + 'ui/web/**/*.{js,jsx,ts,tsx}', + '!**/*.d.ts', + '!**/tests/**', + ], + coverageDirectory: '/coverage/jest', + coverageReporters: ['json', 'text', 'lcov', 'html'], projects: [ { displayName: 'unit-core', diff --git a/package-lock.json b/package-lock.json index ca0524c1b1f..f0fa1c89cbd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,7 @@ "": { "name": "everfreenote", "version": "0.3.0", + "hasInstallScript": true, "dependencies": { "@babel/runtime": "^7.29.2", "@hookform/resolvers": "^5.2.2", diff --git a/package.json b/package.json index d5350312db0..45f5649b528 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,9 @@ "dev": "next dev", "build": "next build", "start": "next start", + "postinstall": "node scripts/patch-gray-matter-compat.js", "test:unit": "jest --config jest.config.cjs --selectProjects unit-core unit-web --passWithNoTests", + "test:unit:coverage": "jest --config jest.config.cjs --selectProjects unit-core integration-core unit-web --coverage --runInBand", "test:unit:core": "jest --config jest.config.cjs --selectProjects unit-core", "test:unit:core:allure": "npm run test:unit:core; test_exit=$?; npm run allure:generate:core-unit; exit $test_exit", "test:integration:core": "jest --config jest.config.cjs --selectProjects integration-core", @@ -39,7 +41,7 @@ "test:component:allure": "npm run test:component; test_exit=$?; npm run allure:generate:component; exit $test_exit", "test:component:coverage": "cypress run --component --browser electron --spec 'cypress/component/**/*.cy.{js,jsx,ts,tsx}' --env codeCoverage=true", "test:component:watch": "cypress open --component --browser electron", - "coverage:component": "nyc report --reporter=html --reporter=text --report-dir=coverage/component", + "coverage:component": "nyc report --temp-dir=.nyc_output --reporter=html --reporter=text --reporter=lcov --report-dir=coverage/component", "allure:generate": "allure generate allure-results --output allure-report", "allure:generate:component": "allure generate allure-results/component --output allure-report/component --report-name \"Web Component Tests\"", "allure:generate:core-unit": "allure generate allure-results/core-unit --output allure-report/core-unit --report-name \"Core Unit Tests\"", @@ -144,6 +146,28 @@ "webpack": ">=5.104.1", "systeminformation": ">=5.31.0" }, + "nyc": { + "all": true, + "report-dir": "coverage/component", + "include": [ + "app/**/*.{js,jsx,ts,tsx}", + "core/**/*.{js,jsx,ts,tsx}", + "ui/web/**/*.{js,jsx,ts,tsx}" + ], + "exclude": [ + "**/*.d.ts", + "**/tests/**", + "cypress/**", + "ui/mobile/**" + ], + "extension": [ + ".js", + ".jsx", + ".ts", + ".tsx" + ], + "excludeAfterRemap": true + }, "devDependencies": { "@cypress/code-coverage": "^4.0.3", "@eslint/compat": "^2.0.0", diff --git a/scripts/patch-gray-matter-compat.js b/scripts/patch-gray-matter-compat.js new file mode 100644 index 00000000000..6520cb37a3b --- /dev/null +++ b/scripts/patch-gray-matter-compat.js @@ -0,0 +1,57 @@ +const fs = require('node:fs') +const path = require('node:path') + +// Why this exists: +// ai-devkit@0.47.0 depends on gray-matter@4.0.3. That gray-matter release +// calls js-yaml's safeLoad/safeDump APIs, while this repository's security +// overrides intentionally install modern js-yaml (5.x), where those aliases +// were removed. As a result, `npx ai-devkit@latest lint` used to fail before +// the DevKit could read any docs. +// +// This is a narrow install-time compatibility patch. It preserves modern +// js-yaml for the whole dependency tree and only changes gray-matter's YAML +// engine selection. The fallback is safe because js-yaml's load/dump APIs are +// the replacements for the old safeLoad/safeDump APIs. + +function patchGrayMatter() { + let packagePath + try { + packagePath = require.resolve('gray-matter/package.json') + } catch (error) { + if (error?.code === 'MODULE_NOT_FOUND') { + console.log('gray-matter is not installed; skipping compatibility patch.') + return + } + throw error + } + + const packageInfo = JSON.parse(fs.readFileSync(packagePath, 'utf8')) + const supportedVersion = '4.0.3' + + // Keep this guard: if ai-devkit or gray-matter is upgraded, the patch must + // be reviewed rather than silently modifying an unknown third-party version. + if (packageInfo.version !== supportedVersion) { + throw new Error( + `Unsupported gray-matter version ${packageInfo.version}; expected ${supportedVersion}. Review the compatibility patch.`, + ) + } + + const enginesPath = path.join(path.dirname(packagePath), 'lib', 'engines.js') + const source = fs.readFileSync(enginesPath, 'utf8') + const patched = source + .replace('yaml.safeLoad.bind(yaml)', '(yaml.safeLoad || yaml.load).bind(yaml)') + .replace('yaml.safeDump.bind(yaml)', '(yaml.safeDump || yaml.dump).bind(yaml)') + + if (patched !== source) { + fs.writeFileSync(enginesPath, patched) + console.log(`Patched gray-matter ${supportedVersion} for modern js-yaml compatibility.`) + } +} + +patchGrayMatter() + +// Removal condition: +// delete this script and the package.json `postinstall` entry once ai-devkit +// ships a gray-matter/js-yaml combination that no longer references +// safeLoad/safeDump (or once gray-matter itself releases that compatibility +// fix). Verify with `npm ci` followed by `npx ai-devkit@latest lint`. diff --git a/sonar-project.properties b/sonar-project.properties index d8c771de9d4..e0709dd68f6 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -1,5 +1,19 @@ -sonar.projectKey=EverFreeNote -sonar.sources=core,ui/web,app -sonar.exclusions=**/node_modules/**,**/dist/**,**/.next/**,ui/mobile/** -sonar.typescript.tsconfigPaths=tsconfig.json +sonar.projectKey=koreyba_EverFreeNote +sonar.organization=koreyba +sonar.projectName=EverFreeNote +sonar.sourceEncoding=UTF-8 + +# Keep CI-based static analysis close to the previous Automatic Analysis scope. +# Test inclusions are automatically excluded from the production-source set. +sonar.sources=. +sonar.tests=. +sonar.test.inclusions=**/tests/**,cypress/**,**/*.test.js,**/*.test.jsx,**/*.test.ts,**/*.test.tsx,**/*.spec.js,**/*.spec.jsx,**/*.spec.ts,**/*.spec.tsx +sonar.exclusions=**/node_modules/**,**/dist/**,**/.next/**,**/build/**,**/out/**,coverage/**,allure-report/**,allure-results/**,.nyc_output/**,.scannerwork/**,.worktrees/** + +# Root Jest, Cypress, and mobile Jest own coverage for the product code. Other +# production areas remain statically analyzed but do not dilute this metric +# until they have a dedicated coverage producer. +sonar.coverage.exclusions=supabase/**,scripts/**,db_audit_scripts/**,extensions/**,cypress/**,tests/**,**/tests/**,**/*.config.js,**/*.config.cjs,**/*.config.mjs,**/*.config.ts,**/*.d.ts + +sonar.typescript.tsconfigPaths=tsconfig.json,tsconfig.tests.json,ui/mobile/tsconfig.sonar.json sonar.javascript.node.maxspace=8096 diff --git a/ui/mobile/jest.config.js b/ui/mobile/jest.config.js index 64308e64689..ef7c7a402d3 100644 --- a/ui/mobile/jest.config.js +++ b/ui/mobile/jest.config.js @@ -1,5 +1,6 @@ /** @type {import('jest').Config} */ const os = require('node:os') +const path = require('node:path') module.exports = { preset: 'jest-expo', @@ -22,6 +23,30 @@ module.exports = { '^@ui/mobile/(.*)$': '/$1', '^@/(.*)$': '/../../$1', }, + // Mobile coverage is a separate producer. It owns the Expo application + // sources; shared core modules imported by mobile tests are also recorded + // naturally by Jest and are deduplicated when Sonar imports all LCOV files. + collectCoverageFrom: [ + '/**/*.{js,jsx,ts,tsx}', + '!/tests/**', + '!/coverage/**', + '!/android/**', + '!/ios/**', + '!/.expo/**', + '!/allure-results/**', + '!/allure-report/**', + '!/**/*.d.ts', + '!/*.config.{js,ts}', + ], + coverageDirectory: '/coverage', + // Sonar scans from the repository root, so LCOV source paths must also be + // repository-relative (ui/mobile/...), not relative to this package. + coverageReporters: [ + 'json', + 'text', + ['lcov', { projectRoot: path.resolve(__dirname, '../..') }], + 'html', + ], clearMocks: true, // React Native/Expo tests can leave native listeners open; force exit prevents hangs. forceExit: true, diff --git a/ui/mobile/package.json b/ui/mobile/package.json index c3832f6ab05..53cddadcbe4 100644 --- a/ui/mobile/package.json +++ b/ui/mobile/package.json @@ -26,7 +26,7 @@ "test": "jest", "test:allure": "jest; test_exit=$?; npm run allure:generate; exit $test_exit", "test:watch": "jest --watch", - "test:coverage": "jest --coverage", + "test:coverage": "jest --coverage --runInBand", "allure:generate": "allure generate allure-results/mobile-unit --output allure-report/mobile-unit --report-name \"Mobile Unit Tests\"", "allure:open": "allure open allure-report/mobile-unit", "type-check": "tsc --noEmit", diff --git a/ui/mobile/tsconfig.sonar.json b/ui/mobile/tsconfig.sonar.json new file mode 100644 index 00000000000..c421fafe162 --- /dev/null +++ b/ui/mobile/tsconfig.sonar.json @@ -0,0 +1,37 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "bundler", + "jsx": "react-native", + "strict": true, + "noEmit": true, + "allowJs": false, + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "esModuleInterop": true, + "skipLibCheck": true, + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "baseUrl": ".", + "paths": { + "@core/*": ["../../core/*"], + "@ui/mobile/*": ["./*"], + "@/*": ["../../*"] + } + }, + "include": ["**/*.ts", "**/*.tsx"], + "exclude": [ + "node_modules", + "coverage", + "android", + "ios", + ".expo", + "allure-results", + "allure-report" + ] +}