diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d98e477c..db46bfda 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,14 +2,14 @@ All pull requests are welcome, there are just a few guidelines you need to follow. -When contributing to this repository, please first discuss the change by creating a new [issue](https://github.com/microsoft/mha/issues) or by replying to an existing one. +When contributing to this repository, please first discuss the change by creating a new [issue](https://github.com/microsoft/MHA/issues) or by replying to an existing one. ## GETTING STARTED * Make sure you have a [GitHub account](https://github.com/signup/free). * Fork the repository, you can [learn about forking on Github](https://help.github.com/articles/fork-a-repo) -* [Clone the repro to your local machine](https://help.github.com/articles/cloning-a-repository/) like so: -```git clone --recursive https://github.com/microsoft/mha.git``` +* [Clone the repo to your local machine](https://help.github.com/articles/cloning-a-repository/) like so: +```git clone --recursive https://github.com/microsoft/MHA.git``` ## MAKING CHANGES @@ -19,6 +19,7 @@ When contributing to this repository, please first discuss the change by creatin ```git checkout -b u/username/topic main``` * *Make sure to substitute your own name and topic in this command* * * Once you have a branch, make your changes and commit them to the local branch. +* If your change adds or modifies major functionality, add or update automated Jest tests in the same pull request. * All submissions require a review and pull requests are how those happen. Consult [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more information on pull requests. @@ -27,6 +28,26 @@ information on pull requests. * Push your changes to a topic branch in your fork of the repository. +## TEST POLICY + +* Major functionality changes must include or update automated Jest tests in the same pull request. +* Test updates must meet coverage requirements. +* See [TESTING.md](./TESTING.md) for test commands, coverage thresholds, and CI details. + +## BEFORE YOU OPEN A PULL REQUEST + +* Install dependencies: `npm install` +* Start local development server when validating UI behavior: `npm run dev-server` +* Run lint locally and fix all issues: `npm run lint` +* Run tests locally and confirm pass: `npm test` +* Include related tests in the same PR when behavior changes and verify coverage requirements are met. + +## SECURITY REPORTING + +* Do not report security vulnerabilities in public issues. +* Follow the Microsoft coordinated vulnerability disclosure policy: + * + ## PUSH TO YOUR FORK AND SUBMIT A PULL REQUEST At this point you're waiting on the code/changes to be reviewed. diff --git a/README.md b/README.md index ca175bdd..1f5c7b08 100644 --- a/README.md +++ b/README.md @@ -7,13 +7,46 @@ [![Jest](https://github.com/microsoft/MHA/actions/workflows/jest.yml/badge.svg)](https://github.com/microsoft/MHA/actions/workflows/jest.yml) [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/microsoft/MHA/badge)](https://scorecard.dev/viewer/?uri=github.com%2Fmicrosoft%2FMHA) -[![OpenSSF Best Practices](https://bestpractices.coreinfrastructure.org/projects/7511/badge)](https://bestpractices.coreinfrastructure.org/projects/7511) +[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/7511/badge)](https://www.bestpractices.dev/projects/7511) Message Header Analyzer mail app. This is the source for the Message Header Analyzer. Install the app from the store here: +## Obtain, Provide Feedback, and Contribute + +- Obtain: Install Message Header Analyzer from AppSource: +- Provide feedback (bugs/enhancements): Open a GitHub issue: +- Contribute code/docs: Submit a pull request to this repository and follow the contribution process in `CONTRIBUTING.md`: + +## External Interface Reference + +This section documents the external interface of the software, including where it is accessed and the expected inputs and outputs. + +### End-user interface + +- AppSource install endpoint: +- Standalone web interface URL: +- Outlook add-in interface: opened from an Outlook message in supported Outlook clients. + +### Functional input/output + +- Primary input: the currently selected email message in Outlook. +- Data input read by the app: transport message headers retrieved through the Outlook/EWS mailbox APIs. +- Primary output: parsed and human-readable header analysis shown in the MHA UI. + +### Local development/test interface + +- Local standalone page: +- Local unit test runner page: +- Local coverage report page: + +### Interface behavior notes + +- Required Outlook permission level for header retrieval: ReadWriteMailbox. +- The app consumes Outlook mailbox APIs and does not expose a separate public REST API. + ## Installation Procedure Because MHA requires the ReadWriteMailbox permission it can only be installed by the Administrator through the Exchange admin center or by a user as a custom addon. Here are some steps I put together: @@ -44,8 +77,7 @@ Here is a standalone Message Header Analyzer running the same backend code as th ## Unit Tests -- [Unit tests](https://mha.azurewebsites.net/Pages/test) -- [Code coverage](https://mha.azurewebsites.net/Pages/coverage/lcov-report) +- Testing policy, local test workflow, coverage requirements, and CI details are documented in [TESTING.md](./TESTING.md). ## Mobile @@ -65,14 +97,6 @@ For both IOS and Android click open an email, then press the three dots under th - Manual build: `npm run build` - For continuous build on changes, you can use watch: `npm run watch` -### Unit Testing - -- Start the dev server: `npm run dev-server` -- Run unit tests from command line: `npm run test` -- View test results here: -- View code coverage here: -- After changing source you will need to `npm run test` again. - ### Live site testing - Start the dev server: `npm run dev-server` diff --git a/TESTING.md b/TESTING.md new file mode 100644 index 00000000..1b3ee555 --- /dev/null +++ b/TESTING.md @@ -0,0 +1,37 @@ +# TESTING + +This document describes MHA testing policy, local test execution, coverage requirements, and CI validation. + +## Test policy + +- Major functionality changes must include or update automated Jest tests in the same pull request. +- When behavior changes, contributors are expected to add or update tests that validate the new behavior. +- Automated tests must include assertion checks (Jest `expect(...)`) for behavior verification. +- Pull requests are validated by CI running `npm test`. +- Global minimum coverage thresholds are enforced in `jest.config.ts`: + - branches: 35 + - functions: 40 + - lines: 40 + - statements: 40 + +## Local testing workflow + +1. Install dependencies: `npm install` +2. Start local development server when validating UI behavior: `npm run dev-server` +3. Run lint and fix issues: `npm run lint` +4. Run tests: `npm test` + +## Test outputs + +- Local unit test runner page: +- Local coverage report page: +- Hosted unit test report: +- Hosted code coverage report: + +## CI test validation + +- Workflow: `.github/workflows/jest.yml` +- Triggered on pull requests, merge groups, workflow dispatch, and pushes to `main`. +- CI executes: + - `npm ci` + - `npm test` diff --git a/package-lock.json b/package-lock.json index 1bd7dc6a..76704cb1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -132,9 +132,9 @@ } }, "node_modules/@apidevtools/swagger-parser/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "dev": true, "peer": true, "dependencies": { @@ -2925,9 +2925,9 @@ } }, "node_modules/@microsoft/app-manifest/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "dev": true, "peer": true, "dependencies": { @@ -3326,9 +3326,9 @@ } }, "node_modules/@microsoft/teams-manifest/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "dev": true, "peer": true, "dependencies": { @@ -3473,9 +3473,9 @@ } }, "node_modules/@microsoft/teamsfx-core/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "dev": true, "peer": true, "dependencies": { @@ -5213,11 +5213,10 @@ } }, "node_modules/ajv-formats/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "dev": true, - "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -5554,14 +5553,14 @@ } }, "node_modules/axios": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", - "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.6.tgz", + "integrity": "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==", "dev": true, "peer": true, "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.4", + "follow-redirects": "^1.15.11", + "form-data": "^4.0.5", "proxy-from-env": "^1.1.0" } }, @@ -6525,9 +6524,9 @@ } }, "node_modules/copy-webpack-plugin/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "dev": true, "dependencies": { "fast-deep-equal": "^3.1.3", @@ -7092,9 +7091,10 @@ } }, "node_modules/diff": { - "version": "4.0.2", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", + "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==", "dev": true, - "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" } @@ -8303,7 +8303,9 @@ "license": "ISC" }, "node_modules/follow-redirects": { - "version": "1.15.9", + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", "dev": true, "funding": [ { @@ -8311,7 +8313,6 @@ "url": "https://github.com/sponsors/RubenVerborgh" } ], - "license": "MIT", "engines": { "node": ">=4.0" }, @@ -8390,11 +8391,10 @@ } }, "node_modules/form-data": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", - "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", "dev": true, - "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -11573,9 +11573,10 @@ } }, "node_modules/mini-css-extract-plugin/node_modules/ajv": { - "version": "8.17.1", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "dev": true, - "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -12427,11 +12428,10 @@ } }, "node_modules/office-addin-manifest/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "dev": true, - "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -15614,11 +15614,10 @@ } }, "node_modules/underscore": { - "version": "1.13.7", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.7.tgz", - "integrity": "sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g==", + "version": "1.13.8", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.8.tgz", + "integrity": "sha512-DXtD3ZtEQzc7M8m4cXotyHR+FAS18C64asBYY5vqZexfYryNNnDc02W4hKg3rdQuqOYas1jkseX0+nZXjTXnvQ==", "dev": true, - "license": "MIT", "peer": true }, "node_modules/undici-types": { @@ -16028,9 +16027,10 @@ } }, "node_modules/webpack-dev-middleware/node_modules/ajv": { - "version": "8.17.1", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "dev": true, - "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -16169,9 +16169,10 @@ } }, "node_modules/webpack-dev-server/node_modules/ajv": { - "version": "8.17.1", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "dev": true, - "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -16352,9 +16353,10 @@ } }, "node_modules/webpack/node_modules/ajv": { - "version": "8.17.1", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "dev": true, - "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1",