From e1b27dfba2305f7874344aca94a0c95578974a14 Mon Sep 17 00:00:00 2001 From: scaminati-bv Date: Fri, 20 Sep 2024 14:52:16 +0200 Subject: [PATCH 1/9] feat: add io integration tests with mock --- .devops/pagopa-code-review-pipelines.yml | 7 ++- .env.development | 6 +- .proxyrc.js | 11 +--- mock/io-mock.js | 2 +- .../final-status-io.integration.test.ts | 61 +++++++++++++++++++ 5 files changed, 74 insertions(+), 13 deletions(-) create mode 100644 src/__integration_tests__/final-status-io.integration.test.ts diff --git a/.devops/pagopa-code-review-pipelines.yml b/.devops/pagopa-code-review-pipelines.yml index 621ab57..00a2434 100644 --- a/.devops/pagopa-code-review-pipelines.yml +++ b/.devops/pagopa-code-review-pipelines.yml @@ -56,7 +56,7 @@ stages: io_api_path: '/ecommerce/webview/v1' npg_sdk_url: 'https://stg-ta.nexigroup.com/monetaweb/resources/hfsdk.js' gdi_check_timeout: 12000 - io_client_redirect_outcome_path: '/pp-restapi/v4' + io_client_redirect_outcome_path: 'https://api.dev.platform.pagopa.it/ecommerce/io-outcomes/v1/transactions' checkout_client_redirect_outcome_path: 'https://dev.checkout.pagopa.it/v2/esito' - script: | @@ -154,6 +154,11 @@ stages: displayName: 'export envs from .env.development' workingDirectory: pagopa-ecommerce-fe + - script: | + yarn start-io-mock & + displayName: 'Start ecommerce fe IO mock' + workingDirectory: pagopa-ecommerce-fe + - script: | yarn dev & displayName: 'Start ecommerce fe' diff --git a/.env.development b/.env.development index a231471..ee5f1aa 100644 --- a/.env.development +++ b/.env.development @@ -5,7 +5,7 @@ ECOMMERCE_CHECKOUT_API_PATH=/ecommerce/checkout/v1 ECOMMERCE_IO_API_PATH=/ecommerce/webview/v1 ECOMMERCE_GDI_CHECK_TIMEOUT=12000 ECOMMERCE_NPG_SDK_URL=https://stg-ta.nexigroup.com/monetaweb/resources/hfsdk.js -ECOMMERCE_IO_CLIENT_REDIRECT_OUTCOME_PATH=test +ECOMMERCE_IO_CLIENT_REDIRECT_OUTCOME_PATH=http://localhost:1234/ecommerce/io-outcomes/v1/transactions ECOMMERCE_CHECKOUT_CLIENT_REDIRECT_OUTCOME_PATH=http://localhost:1234/v2/esito -ECOMMERCE_GET_TRANSACTION_POLLING_DELAY_MILLIS=1000 -ECOMMERCE_GET_TRANSACTION_POLLING_RETRIES=4 \ No newline at end of file +ECOMMERCE_GET_TRANSACTION_POLLING_DELAY_MILLIS=100 +ECOMMERCE_GET_TRANSACTION_POLLING_RETRIES=2 \ No newline at end of file diff --git a/.proxyrc.js b/.proxyrc.js index d8c0bcf..3d95842 100644 --- a/.proxyrc.js +++ b/.proxyrc.js @@ -5,16 +5,14 @@ * and the API proxy server (thanks to http-proxy-middleware) * on localhost:1234 so we don't have to deal with CORS. * - * Note: to run the development server must be set IO_PAY_PORTAL_API_HOST=http://localhost:1234 - * and apiHost with the host api (for example http://localhost:80). */ const {createProxyMiddleware} = require("http-proxy-middleware"); const apiHost = "http://127.0.0.1:8080"; +const apiHostIO = "http://127.0.0.1:8082"; const ecommerceBasepathV1 = "/ecommerce/checkout/v1"; -const ecommerceIOBasepathV1 = "/ecommerce/io/v1"; -const ecommerceIOWebviewBasepathV1 = "/ecommerce/webview/v1"; +const ecommerceIOBasepathV1 = "/ecommerce/webview/v1"; module.exports = function (app) { app.use(createProxyMiddleware(ecommerceBasepathV1, { @@ -22,10 +20,7 @@ module.exports = function (app) { })); app.use(createProxyMiddleware(ecommerceIOBasepathV1, { - target: apiHost, + target: apiHostIO, })); - app.use(createProxyMiddleware(ecommerceIOWebviewBasepathV1, { - target: apiHost, - })); } diff --git a/mock/io-mock.js b/mock/io-mock.js index f0e6424..f9d03b5 100644 --- a/mock/io-mock.js +++ b/mock/io-mock.js @@ -4,7 +4,7 @@ */ const express = require("express"); -const port = 8080; +const port = 8082; const app = express(); app.get("/ecommerce/webview/v1/transactions/:transactionId", (req, res) => { diff --git a/src/__integration_tests__/final-status-io.integration.test.ts b/src/__integration_tests__/final-status-io.integration.test.ts new file mode 100644 index 0000000..191de77 --- /dev/null +++ b/src/__integration_tests__/final-status-io.integration.test.ts @@ -0,0 +1,61 @@ +import "expect-puppeteer"; + +describe("Check final status on IO mapping tests", () => { + /** + * Test input and configuration + */ + + const ECOMMERCE_FE_ESITO_PAGE = "http://localhost:1234/ecommerce-fe/esito#clientId=IO&sessionToken=test&transactionId="; + + /** + * Add all mock flow. Reference to the flow defined into the checkout be mock + */ + const mockTransactionIdsWithExpectedResultMap = new Map([ + ["302054585254587560","0"], + ["302054585254587561","0"], + ["302054585254587562","1"], + ["302054585254587563","0"], + ["302054585254587564","1"], + ["302054585254587565","1"], + ["302054585254587566","1"], + ["302054585254587567","1"], + ["302054585254587568","1"], + ["302054585254587569","4"], + ["302054585254587570","8"], + ["302054585254587571","8"], + ["302054585254587572","1"], + ["302054585254587573","8"], + ["302054585254587574","1"], + ["302054585254587575","2"], + ["302054585254587576","17"], + ["302054585254587577","1"], + ["302054585254587578","0"], + ["302054585254587579","17"] + ]); + + + /** + * Increase default test timeout (80000ms) + * to support entire payment flow + */ + jest.setTimeout(120000); + jest.retryTimes(0); + page.setDefaultNavigationTimeout(120000); + page.setDefaultTimeout(120000); + + beforeAll(async () => { + await page.goto(ECOMMERCE_FE_ESITO_PAGE); + await page.setViewport({ width: 1200, height: 907 }); + }) + + + for (const [transactionId, expectedOutcome] of mockTransactionIdsWithExpectedResultMap) { + it(`TransactionId ${transactionId} with expected outcome: ${expectedOutcome}`, async() => { + console.log(`Executing transactionId: [${transactionId}]. expected outcome: [${expectedOutcome}]`); + await page.goto(ECOMMERCE_FE_ESITO_PAGE + transactionId); + await page.waitForFunction("window.location.pathname.includes('ecommerce/io-outcomes/v1/transactions')") + const pollingOutcome = Number.parseInt(page.url().split("outcome=")[1]); + expect(pollingOutcome).toBe(Number.parseInt(expectedOutcome)); + }) + } +}); \ No newline at end of file From 84ae63e06f21d31cf0c00b1e321157787061d3e7 Mon Sep 17 00:00:00 2001 From: scaminati-bv Date: Mon, 23 Sep 2024 16:40:49 +0200 Subject: [PATCH 2/9] feat: add no redirect integration test --- .devops/pagopa-code-review-pipelines.yml | 9 +++++++ .env.development.noredirect | 11 ++++++++ jest.noredirect.integration.config.js | 12 +++++++++ package.json | 5 +++- .../no-redirect.integration.test.ts | 26 +++++++++++++++++++ 5 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 .env.development.noredirect create mode 100644 jest.noredirect.integration.config.js create mode 100644 src/__integration_tests__/no-redirect.integration.test.ts diff --git a/.devops/pagopa-code-review-pipelines.yml b/.devops/pagopa-code-review-pipelines.yml index 00a2434..dd44c47 100644 --- a/.devops/pagopa-code-review-pipelines.yml +++ b/.devops/pagopa-code-review-pipelines.yml @@ -171,6 +171,15 @@ stages: - script: yarn test:integration displayName: 'Integration tests exec' workingDirectory: pagopa-ecommerce-fe + + - script: | + yarn dev:env:noredirect & + displayName: 'Start ecommerce fe with noredirect env' + workingDirectory: pagopa-ecommerce-fe + + - script: yarn test:noredirect-integration + displayName: 'IO no redirect button Integration tests exec ' + workingDirectory: pagopa-ecommerce-fe - task: PublishTestResults@2 displayName: 'Publish integration tests results from Jest tests' diff --git a/.env.development.noredirect b/.env.development.noredirect new file mode 100644 index 0000000..6c4d00c --- /dev/null +++ b/.env.development.noredirect @@ -0,0 +1,11 @@ +ECOMMERCE_ENV=develop +ECOMMERCE_API_TIMEOUT=10000 +ECOMMERCE_API_HOST=http://localhost:1234 +ECOMMERCE_CHECKOUT_API_PATH=/ecommerce/checkout/v1 +ECOMMERCE_IO_API_PATH=/ecommerce/webview/v1 +ECOMMERCE_GDI_CHECK_TIMEOUT=12000 +ECOMMERCE_NPG_SDK_URL=https://stg-ta.nexigroup.com/monetaweb/resources/hfsdk.js +ECOMMERCE_IO_CLIENT_REDIRECT_OUTCOME_PATH=iowallet://localhost:1234/ecommerce/io-outcomes/v1/transactions +ECOMMERCE_CHECKOUT_CLIENT_REDIRECT_OUTCOME_PATH=http://localhost:1234/v2/esito +ECOMMERCE_GET_TRANSACTION_POLLING_DELAY_MILLIS=100 +ECOMMERCE_GET_TRANSACTION_POLLING_RETRIES=2 \ No newline at end of file diff --git a/jest.noredirect.integration.config.js b/jest.noredirect.integration.config.js new file mode 100644 index 0000000..cdf2a0d --- /dev/null +++ b/jest.noredirect.integration.config.js @@ -0,0 +1,12 @@ +module.exports = { + preset: "jest-puppeteer", + testRegex: "./no-redirect.integration.test\\.ts$", + reporters: [ + 'default', + [ 'jest-junit', { + outputDirectory: './test_reports', + outputName: 'ecommerce-no-redirect-integration-TEST.xml', + } ] + ] + }; + diff --git a/package.json b/package.json index c08b43e..0834ab2 100644 --- a/package.json +++ b/package.json @@ -11,12 +11,15 @@ "prebuild": "npm-run-all generate type-check lint", "build": "rimraf .cache && rimraf dist && parcel build src/index.html --public-url /ecommerce-fe", "dev": "rimraf .cache && rimraf dist && npm-run-all generate type-check dev:env dev:server", + "dev:noredirect": "rimraf .cache && rimraf dist && npm-run-all generate type-check dev:env:noredirect dev:server", "proxy": "npm-run-all generate type-check dev:env dev:proxy", "dev:env": "export $(grep -v '^#' .env.development | xargs) && chmod +x env.sh && ./env.sh", - "dev:server": "parcel src/index.html &", + "dev:env:noredirect": "export $(grep -v '^#' .env.development.noredirect | xargs) && chmod +x env.sh && ./env.sh", + "dev:server": "parcel src/index.html", "dev:proxy": "parcel src/index.html", "test": "jest --config=jest.config.js", "test:integration": "jest --config=jest.integration.config.js", + "test:noredirect-integration": "jest --config=jest.noredirect.integration.config.js", "test:coverage": "jest --coverage --passWithNoTests --config=package.json", "preversion": "auto-changelog --config .auto-changelog.json --unreleased --commit-limit false --stdout --template preview.hbs", "version": "auto-changelog -p --config .auto-changelog.json --unreleased && git add CHANGELOG.md", diff --git a/src/__integration_tests__/no-redirect.integration.test.ts b/src/__integration_tests__/no-redirect.integration.test.ts new file mode 100644 index 0000000..aed6638 --- /dev/null +++ b/src/__integration_tests__/no-redirect.integration.test.ts @@ -0,0 +1,26 @@ +import "expect-puppeteer"; + +describe("Test showing final button for continue to IO", () => { + + const ECOMMERCE_FE_ESITO_PAGE = "http://localhost:1234/ecommerce-fe/esito#clientId=IO&sessionToken=test&transactionId=302054585254587560"; + + /** + * Increase default test timeout (80000ms) + * to support entire payment flow + */ + jest.setTimeout(120000); + jest.retryTimes(0); + page.setDefaultNavigationTimeout(120000); + page.setDefaultTimeout(120000); + + beforeAll(async () => { + await page.setViewport({ width: 1200, height: 907 }); + }) + + + it(`Test IO button on success payment flow`, async() => { + console.log("Start outcome page with IO app redirect") + await page.goto(ECOMMERCE_FE_ESITO_PAGE); + await page.waitForSelector('#continueToIOBtn'); + }) +}); \ No newline at end of file From 5fd4d5d4234cb08c1a57b4494905c396606984d2 Mon Sep 17 00:00:00 2001 From: scaminati-bv Date: Mon, 23 Sep 2024 16:58:30 +0200 Subject: [PATCH 3/9] fix: change test name --- ...edirect.integration.config.js => jest.noredirect.config.js | 4 ++-- package.json | 2 +- .../{no-redirect.integration.test.ts => no-redirect.test.ts} | 0 3 files changed, 3 insertions(+), 3 deletions(-) rename jest.noredirect.integration.config.js => jest.noredirect.config.js (58%) rename src/__integration_tests__/{no-redirect.integration.test.ts => no-redirect.test.ts} (100%) diff --git a/jest.noredirect.integration.config.js b/jest.noredirect.config.js similarity index 58% rename from jest.noredirect.integration.config.js rename to jest.noredirect.config.js index cdf2a0d..bad133e 100644 --- a/jest.noredirect.integration.config.js +++ b/jest.noredirect.config.js @@ -1,11 +1,11 @@ module.exports = { preset: "jest-puppeteer", - testRegex: "./no-redirect.integration.test\\.ts$", + testRegex: "./no-redirect.test\\.ts$", reporters: [ 'default', [ 'jest-junit', { outputDirectory: './test_reports', - outputName: 'ecommerce-no-redirect-integration-TEST.xml', + outputName: 'ecommerce-no-redirect-TEST.xml', } ] ] }; diff --git a/package.json b/package.json index 0834ab2..32cdb8c 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "dev:proxy": "parcel src/index.html", "test": "jest --config=jest.config.js", "test:integration": "jest --config=jest.integration.config.js", - "test:noredirect-integration": "jest --config=jest.noredirect.integration.config.js", + "test:noredirect": "jest --config=jest.noredirect.config.js", "test:coverage": "jest --coverage --passWithNoTests --config=package.json", "preversion": "auto-changelog --config .auto-changelog.json --unreleased --commit-limit false --stdout --template preview.hbs", "version": "auto-changelog -p --config .auto-changelog.json --unreleased && git add CHANGELOG.md", diff --git a/src/__integration_tests__/no-redirect.integration.test.ts b/src/__integration_tests__/no-redirect.test.ts similarity index 100% rename from src/__integration_tests__/no-redirect.integration.test.ts rename to src/__integration_tests__/no-redirect.test.ts From 3d94525b6ac8e0b35369d6df90ab565fcf5ae9d6 Mon Sep 17 00:00:00 2001 From: scaminati-bv Date: Mon, 23 Sep 2024 17:10:35 +0200 Subject: [PATCH 4/9] run code review --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fc3f161..daee789 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ In order to build and run this project are required: ``` ### Usage - + In order to run the application on a local dev server with mock API responses: - ```sh yarn dev From c0bd650c356222e38d65093762b65bef113e469b Mon Sep 17 00:00:00 2001 From: scaminati-bv Date: Mon, 23 Sep 2024 17:26:59 +0200 Subject: [PATCH 5/9] fix: pipeline command noredirect tests --- .devops/pagopa-code-review-pipelines.yml | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.devops/pagopa-code-review-pipelines.yml b/.devops/pagopa-code-review-pipelines.yml index dd44c47..1f30634 100644 --- a/.devops/pagopa-code-review-pipelines.yml +++ b/.devops/pagopa-code-review-pipelines.yml @@ -177,7 +177,7 @@ stages: displayName: 'Start ecommerce fe with noredirect env' workingDirectory: pagopa-ecommerce-fe - - script: yarn test:noredirect-integration + - script: yarn test:noredirect displayName: 'IO no redirect button Integration tests exec ' workingDirectory: pagopa-ecommerce-fe diff --git a/README.md b/README.md index daee789..fc3f161 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ In order to build and run this project are required: ``` ### Usage - + In order to run the application on a local dev server with mock API responses: - ```sh yarn dev From bfa5f14b7f00c9bf487ddef38e7f020202205d86 Mon Sep 17 00:00:00 2001 From: scaminati-bv Date: Mon, 23 Sep 2024 17:48:20 +0200 Subject: [PATCH 6/9] fix: change staps names --- .devops/pagopa-code-review-pipelines.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.devops/pagopa-code-review-pipelines.yml b/.devops/pagopa-code-review-pipelines.yml index 1f30634..7af80ae 100644 --- a/.devops/pagopa-code-review-pipelines.yml +++ b/.devops/pagopa-code-review-pipelines.yml @@ -174,11 +174,11 @@ stages: - script: | yarn dev:env:noredirect & - displayName: 'Start ecommerce fe with noredirect env' + displayName: 'Set ecommerce fe with noredirect env' workingDirectory: pagopa-ecommerce-fe - script: yarn test:noredirect - displayName: 'IO no redirect button Integration tests exec ' + displayName: 'IO noredirect button Integration tests exec' workingDirectory: pagopa-ecommerce-fe - task: PublishTestResults@2 From a5b54a401b6afac22749bca4532df13de8d94daa Mon Sep 17 00:00:00 2001 From: scaminati-bv Date: Tue, 24 Sep 2024 10:43:31 +0200 Subject: [PATCH 7/9] fix: timeout-retry tests values --- .../final-status-io.integration.test.ts | 13 ++++++------- src/__integration_tests__/no-redirect.test.ts | 11 +++++------ 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/__integration_tests__/final-status-io.integration.test.ts b/src/__integration_tests__/final-status-io.integration.test.ts index 191de77..1d9a0f5 100644 --- a/src/__integration_tests__/final-status-io.integration.test.ts +++ b/src/__integration_tests__/final-status-io.integration.test.ts @@ -34,14 +34,13 @@ describe("Check final status on IO mapping tests", () => { ]); - /** - * Increase default test timeout (80000ms) - * to support entire payment flow + /** + * Default test timeout (80000ms) */ - jest.setTimeout(120000); - jest.retryTimes(0); - page.setDefaultNavigationTimeout(120000); - page.setDefaultTimeout(120000); + jest.setTimeout(80000); + jest.retryTimes(3); + page.setDefaultNavigationTimeout(80000); + page.setDefaultTimeout(80000); beforeAll(async () => { await page.goto(ECOMMERCE_FE_ESITO_PAGE); diff --git a/src/__integration_tests__/no-redirect.test.ts b/src/__integration_tests__/no-redirect.test.ts index aed6638..902f38b 100644 --- a/src/__integration_tests__/no-redirect.test.ts +++ b/src/__integration_tests__/no-redirect.test.ts @@ -5,13 +5,12 @@ describe("Test showing final button for continue to IO", () => { const ECOMMERCE_FE_ESITO_PAGE = "http://localhost:1234/ecommerce-fe/esito#clientId=IO&sessionToken=test&transactionId=302054585254587560"; /** - * Increase default test timeout (80000ms) - * to support entire payment flow + * Default test timeout (80000ms) */ - jest.setTimeout(120000); - jest.retryTimes(0); - page.setDefaultNavigationTimeout(120000); - page.setDefaultTimeout(120000); + jest.setTimeout(80000); + jest.retryTimes(3); + page.setDefaultNavigationTimeout(80000); + page.setDefaultTimeout(80000); beforeAll(async () => { await page.setViewport({ width: 1200, height: 907 }); From ddcdaaaae2a0c255066c02e0076406b94775bbae Mon Sep 17 00:00:00 2001 From: scaminati-bv Date: Tue, 24 Sep 2024 12:09:03 +0200 Subject: [PATCH 8/9] fix: revert & on dev script --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 32cdb8c..a5f0cd9 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "proxy": "npm-run-all generate type-check dev:env dev:proxy", "dev:env": "export $(grep -v '^#' .env.development | xargs) && chmod +x env.sh && ./env.sh", "dev:env:noredirect": "export $(grep -v '^#' .env.development.noredirect | xargs) && chmod +x env.sh && ./env.sh", - "dev:server": "parcel src/index.html", + "dev:server": "parcel src/index.html &", "dev:proxy": "parcel src/index.html", "test": "jest --config=jest.config.js", "test:integration": "jest --config=jest.integration.config.js", From 17ac6abd851067c369bf5e488bc33c0e3d88f646 Mon Sep 17 00:00:00 2001 From: scaminati-bv Date: Tue, 24 Sep 2024 12:35:53 +0200 Subject: [PATCH 9/9] fix: noredirect env override default one --- .env.development.noredirect | 12 +----------- package.json | 4 ++-- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/.env.development.noredirect b/.env.development.noredirect index 6c4d00c..588f022 100644 --- a/.env.development.noredirect +++ b/.env.development.noredirect @@ -1,11 +1 @@ -ECOMMERCE_ENV=develop -ECOMMERCE_API_TIMEOUT=10000 -ECOMMERCE_API_HOST=http://localhost:1234 -ECOMMERCE_CHECKOUT_API_PATH=/ecommerce/checkout/v1 -ECOMMERCE_IO_API_PATH=/ecommerce/webview/v1 -ECOMMERCE_GDI_CHECK_TIMEOUT=12000 -ECOMMERCE_NPG_SDK_URL=https://stg-ta.nexigroup.com/monetaweb/resources/hfsdk.js -ECOMMERCE_IO_CLIENT_REDIRECT_OUTCOME_PATH=iowallet://localhost:1234/ecommerce/io-outcomes/v1/transactions -ECOMMERCE_CHECKOUT_CLIENT_REDIRECT_OUTCOME_PATH=http://localhost:1234/v2/esito -ECOMMERCE_GET_TRANSACTION_POLLING_DELAY_MILLIS=100 -ECOMMERCE_GET_TRANSACTION_POLLING_RETRIES=2 \ No newline at end of file +ECOMMERCE_IO_CLIENT_REDIRECT_OUTCOME_PATH=iowallet://localhost:1234/ecommerce/io-outcomes/v1/transactions \ No newline at end of file diff --git a/package.json b/package.json index a5f0cd9..46a676d 100644 --- a/package.json +++ b/package.json @@ -11,10 +11,10 @@ "prebuild": "npm-run-all generate type-check lint", "build": "rimraf .cache && rimraf dist && parcel build src/index.html --public-url /ecommerce-fe", "dev": "rimraf .cache && rimraf dist && npm-run-all generate type-check dev:env dev:server", - "dev:noredirect": "rimraf .cache && rimraf dist && npm-run-all generate type-check dev:env:noredirect dev:server", "proxy": "npm-run-all generate type-check dev:env dev:proxy", + "proxy:noredirect": "npm-run-all generate type-check dev:env:noredirect dev:proxy", "dev:env": "export $(grep -v '^#' .env.development | xargs) && chmod +x env.sh && ./env.sh", - "dev:env:noredirect": "export $(grep -v '^#' .env.development.noredirect | xargs) && chmod +x env.sh && ./env.sh", + "dev:env:noredirect": "export $(grep -vh '^#' .env.development .env.development.noredirect | xargs) && chmod +x env.sh && ./env.sh", "dev:server": "parcel src/index.html &", "dev:proxy": "parcel src/index.html", "test": "jest --config=jest.config.js",