From 2b07f723f824ad0566e67c4999e742de93b73f2f Mon Sep 17 00:00:00 2001 From: Brock Anderson Date: Mon, 6 Nov 2023 17:51:33 -0800 Subject: [PATCH 01/49] added moment.js as dependency --- backend/package-lock.json | 9 +++++++++ backend/package.json | 1 + 2 files changed, 10 insertions(+) diff --git a/backend/package-lock.json b/backend/package-lock.json index ab1e6afee..f03393b77 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -26,6 +26,7 @@ "jsonwebtoken": "^9.0.1", "lodash": "^4.17.21", "memory-cache": "^0.2.0", + "moment": "^2.29.4", "morgan": "^1.10.0", "nconf": "^0.12.0", "nocache": "^3.0.4", @@ -4844,6 +4845,14 @@ "node": "*" } }, + "node_modules/moment": { + "version": "2.29.4", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", + "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", + "engines": { + "node": "*" + } + }, "node_modules/morgan": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", diff --git a/backend/package.json b/backend/package.json index 5b767045f..0a87d28ed 100644 --- a/backend/package.json +++ b/backend/package.json @@ -21,6 +21,7 @@ "jsonwebtoken": "^9.0.1", "lodash": "^4.17.21", "memory-cache": "^0.2.0", + "moment": "^2.29.4", "morgan": "^1.10.0", "nconf": "^0.12.0", "nocache": "^3.0.4", From 805eba8513d5f2306868723f34afb1a8e2929100 Mon Sep 17 00:00:00 2001 From: Brock Anderson Date: Mon, 6 Nov 2023 17:53:28 -0800 Subject: [PATCH 02/49] migration to insert data into 'employee_count_range' table --- .../V1.0.2__employee_count_range.sql | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 backend/db/migrations/V1.0.2__employee_count_range.sql diff --git a/backend/db/migrations/V1.0.2__employee_count_range.sql b/backend/db/migrations/V1.0.2__employee_count_range.sql new file mode 100644 index 000000000..079fbeec6 --- /dev/null +++ b/backend/db/migrations/V1.0.2__employee_count_range.sql @@ -0,0 +1,49 @@ +/* + Update Summary: + - this migration inserts initial data into the 'employee_count_range' table +*/ + +insert into pay_transparency.employee_count_range ( + employee_count_range_id, + employee_count_range, + create_user, + update_user, + expiry_date +) +values ( + gen_random_uuid(), + '50-299', + user, + user, + TO_DATE('9999-12-31', 'YYYY-MM-DD') +); + +insert into pay_transparency.employee_count_range ( + employee_count_range_id, + employee_count_range, + create_user, + update_user, + expiry_date +) +values ( + gen_random_uuid(), + '300-999', + user, + user, + TO_DATE('9999-12-31', 'YYYY-MM-DD') +); + +insert into pay_transparency.employee_count_range ( + employee_count_range_id, + employee_count_range, + create_user, + update_user, + expiry_date +) +values ( + gen_random_uuid(), + '1000 or more', + user, + user, + TO_DATE('9999-12-31', 'YYYY-MM-DD') +); \ No newline at end of file From 7e565131105d1754a80f4e5894675e0f0379ee7c Mon Sep 17 00:00:00 2001 From: Brock Anderson Date: Mon, 6 Nov 2023 17:55:40 -0800 Subject: [PATCH 03/49] added a new route '/codes/employee_count_range'. created a new 'code-service' to support the implementation of this route. added a test case for the new service. --- backend/src/app.ts | 37 +++++++------- backend/src/v1/routes/code-routes.ts | 13 +++++ backend/src/v1/services/code-service.spec.ts | 52 +++++++++++++++++++ backend/src/v1/services/code-service.ts | 53 ++++++++++++++++++++ 4 files changed, 138 insertions(+), 17 deletions(-) create mode 100644 backend/src/v1/routes/code-routes.ts create mode 100644 backend/src/v1/services/code-service.spec.ts create mode 100644 backend/src/v1/services/code-service.ts diff --git a/backend/src/app.ts b/backend/src/app.ts index 846c442c5..d695ab9da 100644 --- a/backend/src/app.ts +++ b/backend/src/app.ts @@ -1,23 +1,24 @@ import express from "express"; -import morgan from "morgan"; -import helmet from "helmet"; -import cors from "cors"; import bodyParser from "body-parser"; -const app = express(); -const apiRouter = express.Router(); -import {fileUploadRouter} from './v1/routes/file-upload-routes'; +import cors from "cors"; +import session from 'express-session'; +import helmet from "helmet"; +import morgan from "morgan"; import noCache from 'nocache'; -import {config} from './config'; import passport from 'passport'; -import {auth} from './v1/services/auth-service'; -import {utils} from './v1/services/utils-service'; -import session from 'express-session'; -import {resolve} from 'path'; +import { resolve } from 'path'; +import { config } from './config'; import authRouter from './v1/routes/auth-routes'; +import codeRouter from './v1/routes/code-routes'; +import { fileUploadRouter } from './v1/routes/file-upload-routes'; import userRouter from './v1/routes/user-info-routes'; +import { auth } from './v1/services/auth-service'; +import { utils } from './v1/services/utils-service'; +const app = express(); +const apiRouter = express.Router(); -import {logger} from "./logger"; +import { logger } from "./logger"; const JWTStrategy = require('passport-jwt').Strategy; const ExtractJwt = require('passport-jwt').ExtractJwt; const OidcStrategy = require('passport-openidconnect-keycloak-idp').Strategy; @@ -34,7 +35,7 @@ app.use(noCache()); //tells the app to use json as means of transporting data -app.use(bodyParser.json({limit: "50mb", extended: true})); +app.use(bodyParser.json({ limit: "50mb", extended: true })); app.use(bodyParser.urlencoded({ extended: true, limit: "50mb" @@ -47,13 +48,13 @@ const cookie = { //sets cookies for security purposes (prevent cookie access, allow secure connections only, etc) -const sess= { +const sess = { name: 'fin_pay_transparency_cookie', secret: config.get('oidc:clientSecret'), resave: false, saveUninitialized: true, cookie: cookie, - store: new fileSession({path: resolve('./', config.get('server:sessionPath'))}), + store: new fileSession({ path: resolve('./', config.get('server:sessionPath')) }), }; if ('production' === config.get('environment')) { app.set("trust proxy", 1); @@ -89,7 +90,7 @@ function addLoginPassportUse(discovery, strategyName, callbackURI, kc_idp_hint) profile.jwt = accessToken; profile._json = parseJwt(accessToken); profile.refreshToken = refreshToken; - profile.idToken= idToken; + profile.idToken = idToken; return done(null, profile); })); } @@ -149,4 +150,6 @@ apiRouter.get("/", (req, res, next) => { apiRouter.use('/auth', authRouter); apiRouter.use('/user', userRouter); apiRouter.use("/v1/file-upload", fileUploadRouter); -export {app}; +apiRouter.use("/v1/codes", codeRouter); +export { app }; + diff --git a/backend/src/v1/routes/code-routes.ts b/backend/src/v1/routes/code-routes.ts new file mode 100644 index 000000000..39d332370 --- /dev/null +++ b/backend/src/v1/routes/code-routes.ts @@ -0,0 +1,13 @@ +import express from 'express'; +import passport from 'passport'; +import { auth } from "../services/auth-service"; +import { codeService } from '../services/code-service'; + +const isValidBackendToken = auth.isValidBackendToken(); +const router = express.Router(); + +router.get('/employee_count_range ', passport.authenticate('jwt', { session: false }), isValidBackendToken, async (req, res) => { + res.sendStatus(200).json(codeService.getAllEmployeeCountRanges); +}); + +export = router; diff --git a/backend/src/v1/services/code-service.spec.ts b/backend/src/v1/services/code-service.spec.ts new file mode 100644 index 000000000..547bf4948 --- /dev/null +++ b/backend/src/v1/services/code-service.spec.ts @@ -0,0 +1,52 @@ +import prisma from '../prisma/prisma-client'; +import { codeService } from './code-service'; + +jest.mock('../prisma/prisma-client', () => { + return { + employee_count_range: { + findMany: jest.fn() + } + } +}) + +afterEach(() => { + jest.clearAllMocks(); +}); + + +describe("getAllEmployeeCountRanges", () => { + it("returns an array of code values", async () => { + (prisma.employee_count_range.findMany as jest.Mock).mockResolvedValue([ + { + employee_count_range_id: 'ea8b2547-4e93-4bfa-aec1-3e90f91027dd', + employee_count_range: '1-99' + }, + { + employee_count_range_id: 'c7e1c454-7db9-46c6-b250-1567a543d22f', + employee_count_range: '100-499' + }, + { + employee_count_range_id: '5f26cc90-7960-4e14-9700-87ecd75f0a0f', + employee_count_range: '500+' + }, + ]) + + const resp1 = await codeService.getAllEmployeeCountRanges(); + const values1 = resp1.map((d: any) => d.employee_count_range); + expect(resp1.length).toBe(3); + expect(values1).toContain("1-99"); + expect(values1).toContain("100-499"); + expect(values1).toContain("500+"); + expect(prisma.employee_count_range.findMany).toBeCalledTimes(1); + + //repeat the call to confirm that only one first call caused a DB + //query, and the second call returned cached data + const resp2 = await codeService.getAllEmployeeCountRanges(); + const values2 = resp1.map((d: any) => d.employee_count_range); + expect(resp2.length).toBe(3); + expect(values2).toContain("1-99"); + expect(values2).toContain("100-499"); + expect(values2).toContain("500+"); + expect(prisma.employee_count_range.findMany).toBeCalledTimes(1); + }) +}) diff --git a/backend/src/v1/services/code-service.ts b/backend/src/v1/services/code-service.ts new file mode 100644 index 000000000..bb1c8db4d --- /dev/null +++ b/backend/src/v1/services/code-service.ts @@ -0,0 +1,53 @@ +import moment from 'moment'; +import prisma from '../prisma/prisma-client'; + +const cacheExpirationTimeHours = 25; + +let employeeCountRangeCache: any[] = []; +let employeeCountRangeCacheExpiryDate: moment.Moment = null; + +const codeService = { + + /* This function returns a list of untyped objects representing + employee count ranges. + The employee count ranges are stored in the database. Because the values + change infrequently, we reduce roundtrips to the database by caching (in memory) + the full list of all values. */ + async getAllEmployeeCountRanges() { + + const now = moment(); + + if (employeeCountRangeCacheExpiryDate && moment(now).isAfter(employeeCountRangeCacheExpiryDate)) { + //Cache has expired. Clear it + employeeCountRangeCache = []; + employeeCountRangeCacheExpiryDate = null; + } + + //No cached values, so fetch from database + if (!employeeCountRangeCache || !employeeCountRangeCache.length) { + employeeCountRangeCache = await prisma.employee_count_range.findMany({ + select: { + employee_count_range_id: true, + employee_count_range: true + }, + where: { + effective_date: { + lte: now.toDate(), + }, + expiry_date: { + gt: now.toDate(), + }, + } + }); + + //Set the cache to expire at some time in the future. + employeeCountRangeCacheExpiryDate = moment().add(cacheExpirationTimeHours, "hours") + } + + return employeeCountRangeCache; + } + +}; + +export { codeService }; + From 9b74fcaa08ca4cf94b8a4956a385559bb286ba78 Mon Sep 17 00:00:00 2001 From: Brock Anderson Date: Tue, 7 Nov 2023 08:55:42 -0800 Subject: [PATCH 04/49] replaced underscores with dashes in the route '//codes/employee-count-range' to be consistent with the '/file-upload' endpoint. --- backend/src/v1/routes/code-routes.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/v1/routes/code-routes.ts b/backend/src/v1/routes/code-routes.ts index 39d332370..c19217686 100644 --- a/backend/src/v1/routes/code-routes.ts +++ b/backend/src/v1/routes/code-routes.ts @@ -6,7 +6,7 @@ import { codeService } from '../services/code-service'; const isValidBackendToken = auth.isValidBackendToken(); const router = express.Router(); -router.get('/employee_count_range ', passport.authenticate('jwt', { session: false }), isValidBackendToken, async (req, res) => { +router.get('/employee-count-range ', passport.authenticate('jwt', { session: false }), isValidBackendToken, async (req, res) => { res.sendStatus(200).json(codeService.getAllEmployeeCountRanges); }); From 08aeb788b6161c62e68571767809c3520d341c65 Mon Sep 17 00:00:00 2001 From: Brock Anderson Date: Tue, 7 Nov 2023 10:58:59 -0800 Subject: [PATCH 05/49] removed debug statement --- backend/src/v1/services/auth-service.spec.ts | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/backend/src/v1/services/auth-service.spec.ts b/backend/src/v1/services/auth-service.spec.ts index 83a2e967d..6fe5d2b22 100644 --- a/backend/src/v1/services/auth-service.spec.ts +++ b/backend/src/v1/services/auth-service.spec.ts @@ -1,8 +1,8 @@ import axios from 'axios'; import jsonwebtoken from 'jsonwebtoken'; +import { config } from '../../config'; import { auth } from './auth-service'; import { utils } from './utils-service'; -import { config } from '../../config'; //Mock the entire axios module so we never inadvertently make real //HTTP calls to remote services @@ -42,7 +42,6 @@ jest.mock('./utils-service', () => { jest.mock('../../config') const actualConfig = jest.requireActual('../../config').config -console.log(actualConfig) afterEach(() => { jest.clearAllMocks(); @@ -263,13 +262,15 @@ describe("refreshJWT", () => { describe("generateUiToken", () => { it("generates a new JWT token that expires in 30 minute (1800 seconds)", async () => { - (config.get as jest.Mock).mockImplementation((key) => { return { - "tokenGenerate:issuer": "issuer", - "server:frontend": "server-frontend", - "tokenGenerate:audience": "audience", - "tokenGenerate:privateKey": actualConfig.get("tokenGenerate:privateKey") - }[key]}) - + (config.get as jest.Mock).mockImplementation((key) => { + return { + "tokenGenerate:issuer": "issuer", + "server:frontend": "server-frontend", + "tokenGenerate:audience": "audience", + "tokenGenerate:privateKey": actualConfig.get("tokenGenerate:privateKey") + }[key] + }) + const token = auth.generateUiToken(); const payload = jsonwebtoken.decode(token); From 628ac140e771051d99948855ee103407d73518ab Mon Sep 17 00:00:00 2001 From: Brock Anderson Date: Tue, 7 Nov 2023 11:43:29 -0800 Subject: [PATCH 06/49] made employee_count_range.expiry_date an optional column with null as the default value. removed the 'expiry_date' column from insert statements (so nulls are inserted) --- .../V1.0.2__employee_count_range.sql | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/backend/db/migrations/V1.0.2__employee_count_range.sql b/backend/db/migrations/V1.0.2__employee_count_range.sql index 079fbeec6..6cba35f1b 100644 --- a/backend/db/migrations/V1.0.2__employee_count_range.sql +++ b/backend/db/migrations/V1.0.2__employee_count_range.sql @@ -1,49 +1,48 @@ /* Update Summary: - - this migration inserts initial data into the 'employee_count_range' table + - drops a not null constraint on pay_transparency.employee_count_range.expiry_date and changes + the default value to null. + - inserts initial data into the 'employee_count_range' table */ +alter table pay_transparency.employee_count_range alter column expiry_date drop not null; +alter table pay_transparency.employee_count_range alter column expiry_date set default null; + insert into pay_transparency.employee_count_range ( employee_count_range_id, employee_count_range, create_user, - update_user, - expiry_date + update_user ) values ( gen_random_uuid(), '50-299', user, - user, - TO_DATE('9999-12-31', 'YYYY-MM-DD') + user ); insert into pay_transparency.employee_count_range ( employee_count_range_id, employee_count_range, create_user, - update_user, - expiry_date + update_user ) values ( gen_random_uuid(), '300-999', user, - user, - TO_DATE('9999-12-31', 'YYYY-MM-DD') + user ); insert into pay_transparency.employee_count_range ( employee_count_range_id, employee_count_range, create_user, - update_user, - expiry_date + update_user ) values ( gen_random_uuid(), '1000 or more', user, - user, - TO_DATE('9999-12-31', 'YYYY-MM-DD') + user ); \ No newline at end of file From 3f716737f51a958c799b3764deaf2b26fa893884 Mon Sep 17 00:00:00 2001 From: Brock Anderson Date: Tue, 7 Nov 2023 11:47:54 -0800 Subject: [PATCH 07/49] updated column employee_count_range.expiry_date to be optional with no default --- backend/src/v1/prisma/schema.prisma | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/v1/prisma/schema.prisma b/backend/src/v1/prisma/schema.prisma index 99dd122bb..803571ee2 100644 --- a/backend/src/v1/prisma/schema.prisma +++ b/backend/src/v1/prisma/schema.prisma @@ -26,7 +26,7 @@ model employee_count_range { create_date DateTime @default(now()) @db.Timestamp(6) update_date DateTime @default(now()) @db.Timestamp(6) effective_date DateTime @default(now()) @db.Timestamp(6) - expiry_date DateTime @default(now()) @db.Timestamp(6) + expiry_date DateTime? @db.Timestamp(6) create_user String @db.VarChar(255) update_user String @db.VarChar(255) pay_transparency_report pay_transparency_report[] From 125ea89a846e0cddc2d3c455526d32db258f7d93 Mon Sep 17 00:00:00 2001 From: Brock Anderson Date: Tue, 7 Nov 2023 11:48:54 -0800 Subject: [PATCH 08/49] code cleanup to test for getAllEmployeeCountRanges() --- backend/src/v1/services/code-service.spec.ts | 33 ++++++++++++-------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/backend/src/v1/services/code-service.spec.ts b/backend/src/v1/services/code-service.spec.ts index 547bf4948..cfac5f250 100644 --- a/backend/src/v1/services/code-service.spec.ts +++ b/backend/src/v1/services/code-service.spec.ts @@ -16,7 +16,7 @@ afterEach(() => { describe("getAllEmployeeCountRanges", () => { it("returns an array of code values", async () => { - (prisma.employee_count_range.findMany as jest.Mock).mockResolvedValue([ + const mockDBResp = [ { employee_count_range_id: 'ea8b2547-4e93-4bfa-aec1-3e90f91027dd', employee_count_range: '1-99' @@ -29,24 +29,31 @@ describe("getAllEmployeeCountRanges", () => { employee_count_range_id: '5f26cc90-7960-4e14-9700-87ecd75f0a0f', employee_count_range: '500+' }, - ]) + ]; + (prisma.employee_count_range.findMany as jest.Mock).mockResolvedValue(mockDBResp) + + // Expect the first call to the function to cause the implementation + // provide a response by fetching data from a database (also confirm the + //response contains the expected data) const resp1 = await codeService.getAllEmployeeCountRanges(); const values1 = resp1.map((d: any) => d.employee_count_range); - expect(resp1.length).toBe(3); - expect(values1).toContain("1-99"); - expect(values1).toContain("100-499"); - expect(values1).toContain("500+"); expect(prisma.employee_count_range.findMany).toBeCalledTimes(1); - - //repeat the call to confirm that only one first call caused a DB - //query, and the second call returned cached data + expect(resp1.length).toBe(3); + expect(values1).toContain(mockDBResp[0].employee_count_range); + expect(values1).toContain(mockDBResp[1].employee_count_range); + expect(values1).toContain(mockDBResp[2].employee_count_range); + + // Repeat the call to confirm that only one first call caused a DB + // query, and the second call returned cached data (also confirm the + //response contains the expected data) const resp2 = await codeService.getAllEmployeeCountRanges(); const values2 = resp1.map((d: any) => d.employee_count_range); - expect(resp2.length).toBe(3); - expect(values2).toContain("1-99"); - expect(values2).toContain("100-499"); - expect(values2).toContain("500+"); expect(prisma.employee_count_range.findMany).toBeCalledTimes(1); + expect(resp2.length).toBe(3); + expect(values2).toContain(mockDBResp[0].employee_count_range); + expect(values2).toContain(mockDBResp[1].employee_count_range); + expect(values2).toContain(mockDBResp[2].employee_count_range); + }) }) From 908580ef90c7c2a1685e68a40ca5aabf36b95ffb Mon Sep 17 00:00:00 2001 From: Brock Anderson Date: Tue, 7 Nov 2023 11:50:44 -0800 Subject: [PATCH 09/49] updated query in getAllEmployeeCountRanges() to include records with null expiry_date (if effective_date is valid) --- backend/src/v1/services/code-service.ts | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/backend/src/v1/services/code-service.ts b/backend/src/v1/services/code-service.ts index bb1c8db4d..7c187e476 100644 --- a/backend/src/v1/services/code-service.ts +++ b/backend/src/v1/services/code-service.ts @@ -30,13 +30,23 @@ const codeService = { employee_count_range_id: true, employee_count_range: true }, + //Only fetch records that are within the effective time range + //i.e. current time >= effective date and either no expiry date is given, + //or expiry date is in the future where: { effective_date: { lte: now.toDate(), }, - expiry_date: { - gt: now.toDate(), - }, + OR: [ + { + expiry_date: null + }, + { + expiry_date: { + gt: now.toDate(), + } + } + ] } }); From b144aa07f233d209d1600511fd5c2c0c3c1b3e69 Mon Sep 17 00:00:00 2001 From: Brock Anderson Date: Wed, 8 Nov 2023 13:41:59 -0800 Subject: [PATCH 10/49] fixed merge conflicts --- backend/src/v1/routes/code-routes.ts | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/backend/src/v1/routes/code-routes.ts b/backend/src/v1/routes/code-routes.ts index 98a5eca12..d3d9a3094 100644 --- a/backend/src/v1/routes/code-routes.ts +++ b/backend/src/v1/routes/code-routes.ts @@ -1,22 +1,11 @@ import express from 'express'; import passport from 'passport'; import { auth } from "../services/auth-service"; -<<<<<<< HEAD import { codeService } from '../services/code-service'; -======= -import { codeService } from "../services/code-service"; ->>>>>>> bb0ba5ad6a7c37899f2a4d0e4842636583f0755e const isValidBackendToken = auth.isValidBackendToken(); const router = express.Router(); -<<<<<<< HEAD -router.get('/employee-count-range ', passport.authenticate('jwt', { session: false }), isValidBackendToken, async (req, res) => { - res.sendStatus(200).json(codeService.getAllEmployeeCountRanges); -}); - -export = router; -======= router.get('/employee-count-ranges', passport.authenticate('jwt', { session: false }), isValidBackendToken, async (req, res) => { const body = await codeService.getAllEmployeeCountRanges() res.status(200).json(body); @@ -24,4 +13,3 @@ router.get('/employee-count-ranges', passport.authenticate('jwt', { session: fal export = router; ->>>>>>> bb0ba5ad6a7c37899f2a4d0e4842636583f0755e From 936dadefc7b43a42d782ddc533c644e61743bd1e Mon Sep 17 00:00:00 2001 From: Brock Anderson Date: Wed, 8 Nov 2023 13:42:39 -0800 Subject: [PATCH 11/49] fixed merge conflicts --- backend/src/v1/routes/code-routes.ts | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/backend/src/v1/routes/code-routes.ts b/backend/src/v1/routes/code-routes.ts index 98a5eca12..7aab400f8 100644 --- a/backend/src/v1/routes/code-routes.ts +++ b/backend/src/v1/routes/code-routes.ts @@ -1,22 +1,11 @@ import express from 'express'; import passport from 'passport'; import { auth } from "../services/auth-service"; -<<<<<<< HEAD -import { codeService } from '../services/code-service'; -======= import { codeService } from "../services/code-service"; ->>>>>>> bb0ba5ad6a7c37899f2a4d0e4842636583f0755e const isValidBackendToken = auth.isValidBackendToken(); const router = express.Router(); -<<<<<<< HEAD -router.get('/employee-count-range ', passport.authenticate('jwt', { session: false }), isValidBackendToken, async (req, res) => { - res.sendStatus(200).json(codeService.getAllEmployeeCountRanges); -}); - -export = router; -======= router.get('/employee-count-ranges', passport.authenticate('jwt', { session: false }), isValidBackendToken, async (req, res) => { const body = await codeService.getAllEmployeeCountRanges() res.status(200).json(body); @@ -24,4 +13,3 @@ router.get('/employee-count-ranges', passport.authenticate('jwt', { session: fal export = router; ->>>>>>> bb0ba5ad6a7c37899f2a4d0e4842636583f0755e From ccd366dd726c45ae2d31b8b572c856bce63e9685 Mon Sep 17 00:00:00 2001 From: Brock Anderson Date: Thu, 9 Nov 2023 17:26:29 -0800 Subject: [PATCH 12/49] added a new 'store' to manage retrieval and storage of static content (employee count ranges and naics codes) from the backend. --- frontend/src/store/modules/codeStore.ts | 52 +++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 frontend/src/store/modules/codeStore.ts diff --git a/frontend/src/store/modules/codeStore.ts b/frontend/src/store/modules/codeStore.ts new file mode 100644 index 000000000..de3836281 --- /dev/null +++ b/frontend/src/store/modules/codeStore.ts @@ -0,0 +1,52 @@ +import { defineStore, storeToRefs } from 'pinia'; +import { ref, watch } from 'vue'; +import ApiService from '../../common/apiService.js'; +import { authStore } from './auth.js'; + +/* +The CodeStore houses static data that is ultimately to be used to populate +lists of options in the UI. It is responsible for fetching +those data from the backend and sharing them via a reactive interface. +*/ +export const useCodeStore = defineStore('code', () => { + + const auth = authStore(); + const { isAuthenticated } = storeToRefs(auth) + + const employeeCountRanges = ref([]); + const naicsCodes = ref([]); + + const setEmployeeCountRanges = (val) => { + employeeCountRanges.value = val; + } + const setNaicsCodes = (val) => { + naicsCodes.value = val; + } + + const fetchAllCodes = async () => { + const employeeCountRanges = await ApiService.getEmployeeCountRanges() + setEmployeeCountRanges(employeeCountRanges) + + const naicsCodes = await ApiService.getNaicsCodes() + setNaicsCodes(naicsCodes) + } + + // Watch for changes to the isAuthenticated property in the AuthStore. When set + // to true initiate API calls to fetch static data from the backend. (The API + // calls require a token showing the user is authenticated.) + watch( + isAuthenticated, + async (isAuthenticated) => { + if (isAuthenticated) { + fetchAllCodes(); + } + }, + { immediate: true } + ) + + // Return the public interface for the store + return { + employeeCountRanges, + naicsCodes + } +}) \ No newline at end of file From 4c4c6fb17fda136a69d07e791fb285ada5dbbb18 Mon Sep 17 00:00:00 2001 From: Brock Anderson Date: Thu, 9 Nov 2023 17:27:20 -0800 Subject: [PATCH 13/49] added new functions getEmployeeCountRanges() and getNaicsCodes() to communicate with backend endpoints --- frontend/src/common/apiService.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/frontend/src/common/apiService.js b/frontend/src/common/apiService.js index 05c8ec5f4..40997303c 100644 --- a/frontend/src/common/apiService.js +++ b/frontend/src/common/apiService.js @@ -59,6 +59,26 @@ export default { delete apiAxios.defaults.headers.common['Authorization']; } }, + async getEmployeeCountRanges() { + try{ + const resp = await apiAxios.get(ApiRoutes.EMPLOYEE_COUNT_RANGES); + if (resp?.data) { + return resp.data; + } + throw new Error("Unable to fetch employee count ranges from API"); + } catch(e) { + console.log(`Failed to get employee count ranges from API - ${e}`); + throw e; + } + }, + async getNaicsCodes() { + try{ + return ["1", "2", "3"] + } catch(e) { + console.log(`Failed to get NAICS from API - ${e}`); + throw e; + } + }, async getUserInfo() { try{ return await apiAxios.get(ApiRoutes.USER); From 4c53418de519f714027788cbda57b9b13193617c Mon Sep 17 00:00:00 2001 From: Brock Anderson Date: Thu, 9 Nov 2023 17:28:28 -0800 Subject: [PATCH 14/49] added EMPLOYEE_COUNT_RANGES with path to backend endpoint --- frontend/src/utils/constant.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/utils/constant.js b/frontend/src/utils/constant.js index d7507ecbd..bb4383013 100644 --- a/frontend/src/utils/constant.js +++ b/frontend/src/utils/constant.js @@ -24,7 +24,7 @@ export const ApiRoutes = Object.freeze({ fileUpload: { BASE_URL: fileUploadRoot, }, - + EMPLOYEE_COUNT_RANGES: baseRoot + "/v1/codes/employee-count-ranges" }); export const PAGE_TITLES = Object.freeze({ From 30e8e57ed7f8315b38196e44853045f019d971ed Mon Sep 17 00:00:00 2001 From: Brock Anderson Date: Thu, 9 Nov 2023 17:29:35 -0800 Subject: [PATCH 15/49] made InputForm route require auth --- frontend/src/router.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/router.js b/frontend/src/router.js index cd5a70c56..1091ea741 100644 --- a/frontend/src/router.js +++ b/frontend/src/router.js @@ -43,7 +43,7 @@ const router = createRouter({ component: InputForm, meta: { pageTitle: PAGE_TITLES.REPORT, - requiresAuth: false + requiresAuth: true } }, ] From 214b53b152680fb64c3ee3b5f7a11d4aab735474 Mon Sep 17 00:00:00 2001 From: Brock Anderson Date: Thu, 9 Nov 2023 17:30:11 -0800 Subject: [PATCH 16/49] fixed route from 'Generate Pay Transparency Report' button --- frontend/src/components/Dashboard.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/Dashboard.vue b/frontend/src/components/Dashboard.vue index aeb9df7ee..a070bf325 100644 --- a/frontend/src/components/Dashboard.vue +++ b/frontend/src/components/Dashboard.vue @@ -17,7 +17,7 @@ Then click the button below to access a submission form, where you'll be asked to enter information about your company and upload your CSV file.

- Generate Pay Transparency Report + Generate Pay Transparency Report From 3fa4227df566c56430574a5eb676a82a12fe56b3 Mon Sep 17 00:00:00 2001 From: Brock Anderson Date: Thu, 9 Nov 2023 17:31:40 -0800 Subject: [PATCH 17/49] integrated employee count ranges and company info --- frontend/src/components/InputForm.vue | 49 ++++++++++++++++++--------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/frontend/src/components/InputForm.vue b/frontend/src/components/InputForm.vue index 3fb5e2d25..cb9799bc4 100644 --- a/frontend/src/components/InputForm.vue +++ b/frontend/src/components/InputForm.vue @@ -4,7 +4,6 @@ {{ alertMessage }} - @@ -15,8 +14,8 @@ - - + + @@ -55,29 +54,28 @@ - +

Your Company Information

- + + required disabled> - + + :items="employeeCountRanges" item-title="employee_count_range" required> @@ -118,7 +116,7 @@
- + @@ -138,6 +136,9 @@ import VueDatePicker from '@vuepic/vue-datepicker'; import '@vuepic/vue-datepicker/dist/main.css' import PrimaryButton from './util/PrimaryButton.vue'; import ApiService from '../common/apiService'; +import { useCodeStore } from '../store/modules/codeStore'; +import { authStore } from '../store/modules/auth'; +import { mapState, } from 'pinia'; import moment from 'moment'; export default { @@ -153,6 +154,7 @@ export default { naicsCode: null, naicsCodeList: ["2342"], employeeCount: null, + employeeCountOptions: [], isProcessing: false, uploadFileValue: null, minStartDate: moment().subtract(2, "years").format("yyyy-MM"), @@ -223,8 +225,8 @@ export default { }, watch: { startDate(newVal) { - /* When the startDate changes, automatically adjust the endDate to be - 12 months later */ + // When the startDate changes, automatically adjust the endDate to be + // 12 months later if (newVal) { const startDate = moment(newVal) const endDate = moment(newVal).add(1, 'years').subtract(1, 'months') @@ -232,16 +234,31 @@ export default { } }, endDate(newVal) { - /* When the endDate changes, automatically adjust the startDate to be - 12 months earlier */ + // When the endDate changes, automatically adjust the startDate to be + // 12 months earlier if (newVal) { const endDate = moment(newVal) const startDate = moment(newVal).subtract(1, 'years').add(1, 'months') this.startDate = startDate.format("yyyy-MM"); } - } + }, + userInfo: { + // Watch for changes to userInfo (from the authStore). Copy company name + // and address from that object into state variables in this component. + immediate: true, + handler(userInfo) { + this.companyName = userInfo?.legalName; + const address = `${userInfo?.addressLine1 ? userInfo?.addressLine1 : ""} ${userInfo?.addressLine2 ? userInfo?.addressLine2 : ""}`.trim(); + this.companyAddress = address; + } + }, }, computed: { + ...mapState(useCodeStore, [ + 'employeeCountRanges', + 'naicsCodes' + ]), + ...mapState(authStore, ['userInfo']), dataReady() { return this.validForm && this.uploadFileValue; }, From 4f2a17d5a4623899f589dc083f92d561bf777e3b Mon Sep 17 00:00:00 2001 From: Brock Anderson Date: Fri, 10 Nov 2023 10:29:06 -0800 Subject: [PATCH 18/49] updated the pinia/testing library to help with mocking stores in component tests. upgraded the pinia and vue versions (required to use pinia/testing) --- frontend/package.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index f3bc956a7..01eb0e0be 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -30,7 +30,7 @@ "moment": "^2.29.4", "nconf": "^0.12.0", "path": "^0.12.7", - "pinia": "^2.0.32", + "pinia": "^2.1.7", "prom-client": "^14.1.1", "purgecss-webpack-plugin": "^5.0.0", "regenerator-runtime": "^0.13.11", @@ -40,7 +40,7 @@ "testcafe": "^2.2.0", "v-viewer": "^3.0.11", "vite": "^4.1.4", - "vue": "^3.2.47", + "vue": "^3.3.8", "vue-chartjs": "^5.2.0", "vue-clipboard2": "^0.3.3", "vue-meta": "^3.0.0-alpha.10", @@ -50,6 +50,7 @@ }, "devDependencies": { "@babel/eslint-parser": "^7.19.1", + "@pinia/testing": "^0.1.3", "@vitejs/plugin-vue": "^4.0.0", "@vitest/coverage-v8": "^0.33.0", "@vitest/ui": "^0.33.0", From a6bf2d1c9ee8828bf99a7c26bde99e3ec7fb3482 Mon Sep 17 00:00:00 2001 From: Brock Anderson Date: Fri, 10 Nov 2023 10:29:17 -0800 Subject: [PATCH 19/49] updated the pinia/testing library to help with mocking stores in component tests. upgraded the pinia and vue versions (required to use pinia/testing) --- frontend/package-lock.json | 617 ++++++++++++++++++------------------- 1 file changed, 293 insertions(+), 324 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index a0e609194..34b0a4b3b 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -27,7 +27,7 @@ "moment": "^2.29.4", "nconf": "^0.12.0", "path": "^0.12.7", - "pinia": "^2.0.32", + "pinia": "^2.1.7", "prom-client": "^14.1.1", "purgecss-webpack-plugin": "^5.0.0", "regenerator-runtime": "^0.13.11", @@ -37,7 +37,7 @@ "testcafe": "^2.2.0", "v-viewer": "^3.0.11", "vite": "^4.1.4", - "vue": "^3.2.47", + "vue": "^3.3.8", "vue-chartjs": "^5.2.0", "vue-clipboard2": "^0.3.3", "vue-meta": "^3.0.0-alpha.10", @@ -47,6 +47,7 @@ }, "devDependencies": { "@babel/eslint-parser": "^7.19.1", + "@pinia/testing": "^0.1.3", "@vitejs/plugin-vue": "^4.0.0", "@vitest/coverage-v8": "^0.33.0", "@vitest/ui": "^0.33.0", @@ -605,9 +606,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.21.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.2.tgz", - "integrity": "sha512-URpaIJQwEkEC2T9Kn+Ai6Xe/02iNaVCuT/PtoRz3GPVJVDpPd7mLo+VddTbhCRU9TXqW5mSrQfXZyi8kDKOVpQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.3.tgz", + "integrity": "sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw==", "bin": { "parser": "bin/babel-parser.js" }, @@ -2938,6 +2939,47 @@ "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==", "dev": true }, + "node_modules/@pinia/testing": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@pinia/testing/-/testing-0.1.3.tgz", + "integrity": "sha512-D2Ds2s69kKFaRf2KCcP1NhNZEg5+we59aRyQalwRm7ygWfLM25nDH66267U3hNvRUOTx8ofL24GzodZkOmB5xw==", + "dev": true, + "dependencies": { + "vue-demi": ">=0.14.5" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "pinia": ">=2.1.5" + } + }, + "node_modules/@pinia/testing/node_modules/vue-demi": { + "version": "0.14.6", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.6.tgz", + "integrity": "sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==", + "dev": true, + "hasInstallScript": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, "node_modules/@polka/url": { "version": "1.0.0-next.21", "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.21.tgz", @@ -3182,24 +3224,6 @@ "vitest": ">=0.32.0 <1" } }, - "node_modules/@vitest/coverage-v8/node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true - }, - "node_modules/@vitest/coverage-v8/node_modules/magic-string": { - "version": "0.30.2", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.2.tgz", - "integrity": "sha512-lNZdu7pewtq/ZvWUp9Wpf/x7WzMTsR26TWV03BRZrXFsv+BI6dy8RAiKgm1uM/kyR0rCfUcqvOlXKG66KhIGug==", - "dev": true, - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/@vitest/expect": { "version": "0.33.0", "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-0.33.0.tgz", @@ -3299,24 +3323,6 @@ "url": "https://opencollective.com/vitest" } }, - "node_modules/@vitest/snapshot/node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true - }, - "node_modules/@vitest/snapshot/node_modules/magic-string": { - "version": "0.30.2", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.2.tgz", - "integrity": "sha512-lNZdu7pewtq/ZvWUp9Wpf/x7WzMTsR26TWV03BRZrXFsv+BI6dy8RAiKgm1uM/kyR0rCfUcqvOlXKG66KhIGug==", - "dev": true, - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/@vitest/spy": { "version": "0.33.0", "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-0.33.0.tgz", @@ -3365,49 +3371,49 @@ } }, "node_modules/@vue/compiler-core": { - "version": "3.2.47", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.47.tgz", - "integrity": "sha512-p4D7FDnQb7+YJmO2iPEv0SQNeNzcbHdGByJDsT4lynf63AFkOTFN07HsiRSvjGo0QrxR/o3d0hUyNCUnBU2Tig==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.3.8.tgz", + "integrity": "sha512-hN/NNBUECw8SusQvDSqqcVv6gWq8L6iAktUR0UF3vGu2OhzRqcOiAno0FmBJWwxhYEXRlQJT5XnoKsVq1WZx4g==", "dependencies": { - "@babel/parser": "^7.16.4", - "@vue/shared": "3.2.47", + "@babel/parser": "^7.23.0", + "@vue/shared": "3.3.8", "estree-walker": "^2.0.2", - "source-map": "^0.6.1" + "source-map-js": "^1.0.2" } }, "node_modules/@vue/compiler-dom": { - "version": "3.2.47", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.47.tgz", - "integrity": "sha512-dBBnEHEPoftUiS03a4ggEig74J2YBZ2UIeyfpcRM2tavgMWo4bsEfgCGsu+uJIL/vax9S+JztH8NmQerUo7shQ==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.3.8.tgz", + "integrity": "sha512-+PPtv+p/nWDd0AvJu3w8HS0RIm/C6VGBIRe24b9hSyNWOAPEUosFZ5diwawwP8ip5sJ8n0Pe87TNNNHnvjs0FQ==", "dependencies": { - "@vue/compiler-core": "3.2.47", - "@vue/shared": "3.2.47" + "@vue/compiler-core": "3.3.8", + "@vue/shared": "3.3.8" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.2.47", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.47.tgz", - "integrity": "sha512-rog05W+2IFfxjMcFw10tM9+f7i/+FFpZJJ5XHX72NP9eC2uRD+42M3pYcQqDXVYoj74kHMSEdQ/WmCjt8JFksQ==", - "dependencies": { - "@babel/parser": "^7.16.4", - "@vue/compiler-core": "3.2.47", - "@vue/compiler-dom": "3.2.47", - "@vue/compiler-ssr": "3.2.47", - "@vue/reactivity-transform": "3.2.47", - "@vue/shared": "3.2.47", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.3.8.tgz", + "integrity": "sha512-WMzbUrlTjfYF8joyT84HfwwXo+8WPALuPxhy+BZ6R4Aafls+jDBnSz8PDz60uFhuqFbl3HxRfxvDzrUf3THwpA==", + "dependencies": { + "@babel/parser": "^7.23.0", + "@vue/compiler-core": "3.3.8", + "@vue/compiler-dom": "3.3.8", + "@vue/compiler-ssr": "3.3.8", + "@vue/reactivity-transform": "3.3.8", + "@vue/shared": "3.3.8", "estree-walker": "^2.0.2", - "magic-string": "^0.25.7", - "postcss": "^8.1.10", - "source-map": "^0.6.1" + "magic-string": "^0.30.5", + "postcss": "^8.4.31", + "source-map-js": "^1.0.2" } }, "node_modules/@vue/compiler-ssr": { - "version": "3.2.47", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.47.tgz", - "integrity": "sha512-wVXC+gszhulcMD8wpxMsqSOpvDZ6xKXSVWkf50Guf/S+28hTAXPDYRTbLQ3EDkOP5Xz/+SY37YiwDquKbJOgZw==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.3.8.tgz", + "integrity": "sha512-hXCqQL/15kMVDBuoBYpUnSYT8doDNwsjvm3jTefnXr+ytn294ySnT8NlsFHmTgKNjwpuFy7XVV8yTeLtNl/P6w==", "dependencies": { - "@vue/compiler-dom": "3.2.47", - "@vue/shared": "3.2.47" + "@vue/compiler-dom": "3.3.8", + "@vue/shared": "3.3.8" } }, "node_modules/@vue/devtools-api": { @@ -3416,60 +3422,60 @@ "integrity": "sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q==" }, "node_modules/@vue/reactivity": { - "version": "3.2.47", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.47.tgz", - "integrity": "sha512-7khqQ/75oyyg+N/e+iwV6lpy1f5wq759NdlS1fpAhFXa8VeAIKGgk2E/C4VF59lx5b+Ezs5fpp/5WsRYXQiKxQ==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.3.8.tgz", + "integrity": "sha512-ctLWitmFBu6mtddPyOKpHg8+5ahouoTCRtmAHZAXmolDtuZXfjL2T3OJ6DL6ezBPQB1SmMnpzjiWjCiMYmpIuw==", "dependencies": { - "@vue/shared": "3.2.47" + "@vue/shared": "3.3.8" } }, "node_modules/@vue/reactivity-transform": { - "version": "3.2.47", - "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.47.tgz", - "integrity": "sha512-m8lGXw8rdnPVVIdIFhf0LeQ/ixyHkH5plYuS83yop5n7ggVJU+z5v0zecwEnX7fa7HNLBhh2qngJJkxpwEEmYA==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.3.8.tgz", + "integrity": "sha512-49CvBzmZNtcHua0XJ7GdGifM8GOXoUMOX4dD40Y5DxI3R8OUhMlvf2nvgUAcPxaXiV5MQQ1Nwy09ADpnLQUqRw==", "dependencies": { - "@babel/parser": "^7.16.4", - "@vue/compiler-core": "3.2.47", - "@vue/shared": "3.2.47", + "@babel/parser": "^7.23.0", + "@vue/compiler-core": "3.3.8", + "@vue/shared": "3.3.8", "estree-walker": "^2.0.2", - "magic-string": "^0.25.7" + "magic-string": "^0.30.5" } }, "node_modules/@vue/runtime-core": { - "version": "3.2.47", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.47.tgz", - "integrity": "sha512-RZxbLQIRB/K0ev0K9FXhNbBzT32H9iRtYbaXb0ZIz2usLms/D55dJR2t6cIEUn6vyhS3ALNvNthI+Q95C+NOpA==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.3.8.tgz", + "integrity": "sha512-qurzOlb6q26KWQ/8IShHkMDOuJkQnQcTIp1sdP4I9MbCf9FJeGVRXJFr2mF+6bXh/3Zjr9TDgURXrsCr9bfjUw==", "dependencies": { - "@vue/reactivity": "3.2.47", - "@vue/shared": "3.2.47" + "@vue/reactivity": "3.3.8", + "@vue/shared": "3.3.8" } }, "node_modules/@vue/runtime-dom": { - "version": "3.2.47", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.47.tgz", - "integrity": "sha512-ArXrFTjS6TsDei4qwNvgrdmHtD930KgSKGhS5M+j8QxXrDJYLqYw4RRcDy1bz1m1wMmb6j+zGLifdVHtkXA7gA==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.3.8.tgz", + "integrity": "sha512-Noy5yM5UIf9UeFoowBVgghyGGPIDPy1Qlqt0yVsUdAVbqI8eeMSsTqBtauaEoT2UFXUk5S64aWVNJN4MJ2vRdA==", "dependencies": { - "@vue/runtime-core": "3.2.47", - "@vue/shared": "3.2.47", - "csstype": "^2.6.8" + "@vue/runtime-core": "3.3.8", + "@vue/shared": "3.3.8", + "csstype": "^3.1.2" } }, "node_modules/@vue/server-renderer": { - "version": "3.2.47", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.47.tgz", - "integrity": "sha512-dN9gc1i8EvmP9RCzvneONXsKfBRgqFeFZLurmHOveL7oH6HiFXJw5OGu294n1nHc/HMgTy6LulU/tv5/A7f/LA==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.3.8.tgz", + "integrity": "sha512-zVCUw7RFskvPuNlPn/8xISbrf0zTWsTSdYTsUTN1ERGGZGVnRxM2QZ3x1OR32+vwkkCm0IW6HmJ49IsPm7ilLg==", "dependencies": { - "@vue/compiler-ssr": "3.2.47", - "@vue/shared": "3.2.47" + "@vue/compiler-ssr": "3.3.8", + "@vue/shared": "3.3.8" }, "peerDependencies": { - "vue": "3.2.47" + "vue": "3.3.8" } }, "node_modules/@vue/shared": { - "version": "3.2.47", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.47.tgz", - "integrity": "sha512-BHGyyGN3Q97EZx0taMQ+OLNuZcW3d37ZEVmEAyeoA9ERdGvm9Irc/0Fua8SNyOtV1w6BS4q25wbMzJujO9HIfQ==" + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.8.tgz", + "integrity": "sha512-8PGwybFwM4x8pcfgqEQFy70NaQxASvOC5DJwLQfpArw1UDfUXrJkdxD3BhVTMS+0Lef/TU7YO0Jvr0jJY8T+mw==" }, "node_modules/@vue/test-utils": { "version": "2.4.1", @@ -4778,9 +4784,9 @@ } }, "node_modules/csstype": { - "version": "2.6.21", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz", - "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==" + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" }, "node_modules/cuint": { "version": "0.2.2", @@ -7782,13 +7788,21 @@ } }, "node_modules/magic-string": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", - "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", + "version": "0.30.5", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz", + "integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==", "dependencies": { - "sourcemap-codec": "^1.4.8" + "@jridgewell/sourcemap-codec": "^1.4.15" + }, + "engines": { + "node": ">=12" } }, + "node_modules/magic-string/node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" + }, "node_modules/make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -7985,9 +7999,15 @@ } }, "node_modules/nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -8392,12 +8412,12 @@ } }, "node_modules/pinia": { - "version": "2.0.32", - "resolved": "https://registry.npmjs.org/pinia/-/pinia-2.0.32.tgz", - "integrity": "sha512-8Tw4OrpCSJ028UUyp0gYPP/wyjigLoEceuO/x1G+FlHVf73337e5vLm4uDmrRIoBG1hvaed/eSHnrCFjOc4nkA==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/pinia/-/pinia-2.1.7.tgz", + "integrity": "sha512-+C2AHFtcFqjPih0zpYuvof37SFxMQ7OEG2zV9jRI12i9BOy3YQVAHwdKtyyc8pDcDyIc33WCIsZaCFWU7WWxGQ==", "dependencies": { "@vue/devtools-api": "^6.5.0", - "vue-demi": "*" + "vue-demi": ">=0.14.5" }, "funding": { "url": "https://github.com/sponsors/posva" @@ -8405,7 +8425,7 @@ "peerDependencies": { "@vue/composition-api": "^1.4.0", "typescript": ">=4.4.4", - "vue": "^2.6.14 || ^3.2.0" + "vue": "^2.6.14 || ^3.3.0" }, "peerDependenciesMeta": { "@vue/composition-api": { @@ -8417,9 +8437,9 @@ } }, "node_modules/pinia/node_modules/vue-demi": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.11.tgz", - "integrity": "sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==", + "version": "0.14.6", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.6.tgz", + "integrity": "sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==", "hasInstallScript": true, "bin": { "vue-demi-fix": "bin/vue-demi-fix.js", @@ -8620,9 +8640,9 @@ } }, "node_modules/postcss": { - "version": "8.4.21", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz", - "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==", + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", "funding": [ { "type": "opencollective", @@ -8631,10 +8651,14 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { - "nanoid": "^3.3.4", + "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" }, @@ -9465,12 +9489,6 @@ "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", "deprecated": "See https://github.com/lydell/source-map-url#deprecated" }, - "node_modules/sourcemap-codec": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", - "deprecated": "Please use @jridgewell/sourcemap-codec instead" - }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -11019,12 +11037,6 @@ } } }, - "node_modules/vitest/node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true - }, "node_modules/vitest/node_modules/chai": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz", @@ -11055,28 +11067,24 @@ "node": ">=6" } }, - "node_modules/vitest/node_modules/magic-string": { - "version": "0.30.2", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.2.tgz", - "integrity": "sha512-lNZdu7pewtq/ZvWUp9Wpf/x7WzMTsR26TWV03BRZrXFsv+BI6dy8RAiKgm1uM/kyR0rCfUcqvOlXKG66KhIGug==", - "dev": true, - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/vue": { - "version": "3.2.47", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.2.47.tgz", - "integrity": "sha512-60188y/9Dc9WVrAZeUVSDxRQOZ+z+y5nO2ts9jWXSTkMvayiWxCWOWtBQoYjLeccfXkiiPZWAHcV+WTPhkqJHQ==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.3.8.tgz", + "integrity": "sha512-5VSX/3DabBikOXMsxzlW8JyfeLKlG9mzqnWgLQLty88vdZL7ZJgrdgBOmrArwxiLtmS+lNNpPcBYqrhE6TQW5w==", "dependencies": { - "@vue/compiler-dom": "3.2.47", - "@vue/compiler-sfc": "3.2.47", - "@vue/runtime-dom": "3.2.47", - "@vue/server-renderer": "3.2.47", - "@vue/shared": "3.2.47" + "@vue/compiler-dom": "3.3.8", + "@vue/compiler-sfc": "3.3.8", + "@vue/runtime-dom": "3.3.8", + "@vue/server-renderer": "3.3.8", + "@vue/shared": "3.3.8" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, "node_modules/vue-chartjs": { @@ -11153,9 +11161,9 @@ } }, "node_modules/vue-quick-chat/node_modules/@vue/compiler-sfc": { - "version": "2.7.14", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-2.7.14.tgz", - "integrity": "sha512-aNmNHyLPsw+sVvlQFQ2/8sjNuLtK54TC6cuKnVzAY93ks4ZBrvwQSnkkIh7bsbNhum5hJBS00wSDipQ937f5DA==", + "version": "2.7.15", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-2.7.15.tgz", + "integrity": "sha512-FCvIEevPmgCgqFBH7wD+3B97y7u7oj/Wr69zADBf403Tui377bThTjBvekaZvlRr4IwUAu3M6hYZeULZFJbdYg==", "dependencies": { "@babel/parser": "^7.18.4", "postcss": "^8.4.14", @@ -11169,11 +11177,6 @@ "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.", "hasInstallScript": true }, - "node_modules/vue-quick-chat/node_modules/csstype": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", - "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" - }, "node_modules/vue-quick-chat/node_modules/luxon": { "version": "1.28.1", "resolved": "https://registry.npmjs.org/luxon/-/luxon-1.28.1.tgz", @@ -11183,11 +11186,11 @@ } }, "node_modules/vue-quick-chat/node_modules/vue": { - "version": "2.7.14", - "resolved": "https://registry.npmjs.org/vue/-/vue-2.7.14.tgz", - "integrity": "sha512-b2qkFyOM0kwqWFuQmgd4o+uHGU7T+2z3T+WQp8UBjADfEv2n4FEMffzBmCKNP0IGzOEEfYjvtcC62xaSKeQDrQ==", + "version": "2.7.15", + "resolved": "https://registry.npmjs.org/vue/-/vue-2.7.15.tgz", + "integrity": "sha512-a29fsXd2G0KMRqIFTpRgpSbWaNBK3lpCTOLuGLEDnlHWdjB8fwl6zyYZ8xCrqkJdatwZb4mGHiEfJjnw0Q6AwQ==", "dependencies": { - "@vue/compiler-sfc": "2.7.14", + "@vue/compiler-sfc": "2.7.15", "csstype": "^3.1.0" } }, @@ -12093,9 +12096,9 @@ } }, "@babel/parser": { - "version": "7.21.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.2.tgz", - "integrity": "sha512-URpaIJQwEkEC2T9Kn+Ai6Xe/02iNaVCuT/PtoRz3GPVJVDpPd7mLo+VddTbhCRU9TXqW5mSrQfXZyi8kDKOVpQ==" + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.3.tgz", + "integrity": "sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw==" }, "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { "version": "7.18.6", @@ -13622,6 +13625,24 @@ "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==", "dev": true }, + "@pinia/testing": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@pinia/testing/-/testing-0.1.3.tgz", + "integrity": "sha512-D2Ds2s69kKFaRf2KCcP1NhNZEg5+we59aRyQalwRm7ygWfLM25nDH66267U3hNvRUOTx8ofL24GzodZkOmB5xw==", + "dev": true, + "requires": { + "vue-demi": ">=0.14.5" + }, + "dependencies": { + "vue-demi": { + "version": "0.14.6", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.6.tgz", + "integrity": "sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==", + "dev": true, + "requires": {} + } + } + }, "@polka/url": { "version": "1.0.0-next.21", "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.21.tgz", @@ -13849,23 +13870,6 @@ "std-env": "^3.3.3", "test-exclude": "^6.0.0", "v8-to-istanbul": "^9.1.0" - }, - "dependencies": { - "@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true - }, - "magic-string": { - "version": "0.30.2", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.2.tgz", - "integrity": "sha512-lNZdu7pewtq/ZvWUp9Wpf/x7WzMTsR26TWV03BRZrXFsv+BI6dy8RAiKgm1uM/kyR0rCfUcqvOlXKG66KhIGug==", - "dev": true, - "requires": { - "@jridgewell/sourcemap-codec": "^1.4.15" - } - } } }, "@vitest/expect": { @@ -13942,23 +13946,6 @@ "magic-string": "^0.30.1", "pathe": "^1.1.1", "pretty-format": "^29.5.0" - }, - "dependencies": { - "@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true - }, - "magic-string": { - "version": "0.30.2", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.2.tgz", - "integrity": "sha512-lNZdu7pewtq/ZvWUp9Wpf/x7WzMTsR26TWV03BRZrXFsv+BI6dy8RAiKgm1uM/kyR0rCfUcqvOlXKG66KhIGug==", - "dev": true, - "requires": { - "@jridgewell/sourcemap-codec": "^1.4.15" - } - } } }, "@vitest/spy": { @@ -13997,49 +13984,49 @@ } }, "@vue/compiler-core": { - "version": "3.2.47", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.47.tgz", - "integrity": "sha512-p4D7FDnQb7+YJmO2iPEv0SQNeNzcbHdGByJDsT4lynf63AFkOTFN07HsiRSvjGo0QrxR/o3d0hUyNCUnBU2Tig==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.3.8.tgz", + "integrity": "sha512-hN/NNBUECw8SusQvDSqqcVv6gWq8L6iAktUR0UF3vGu2OhzRqcOiAno0FmBJWwxhYEXRlQJT5XnoKsVq1WZx4g==", "requires": { - "@babel/parser": "^7.16.4", - "@vue/shared": "3.2.47", + "@babel/parser": "^7.23.0", + "@vue/shared": "3.3.8", "estree-walker": "^2.0.2", - "source-map": "^0.6.1" + "source-map-js": "^1.0.2" } }, "@vue/compiler-dom": { - "version": "3.2.47", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.47.tgz", - "integrity": "sha512-dBBnEHEPoftUiS03a4ggEig74J2YBZ2UIeyfpcRM2tavgMWo4bsEfgCGsu+uJIL/vax9S+JztH8NmQerUo7shQ==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.3.8.tgz", + "integrity": "sha512-+PPtv+p/nWDd0AvJu3w8HS0RIm/C6VGBIRe24b9hSyNWOAPEUosFZ5diwawwP8ip5sJ8n0Pe87TNNNHnvjs0FQ==", "requires": { - "@vue/compiler-core": "3.2.47", - "@vue/shared": "3.2.47" + "@vue/compiler-core": "3.3.8", + "@vue/shared": "3.3.8" } }, "@vue/compiler-sfc": { - "version": "3.2.47", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.47.tgz", - "integrity": "sha512-rog05W+2IFfxjMcFw10tM9+f7i/+FFpZJJ5XHX72NP9eC2uRD+42M3pYcQqDXVYoj74kHMSEdQ/WmCjt8JFksQ==", - "requires": { - "@babel/parser": "^7.16.4", - "@vue/compiler-core": "3.2.47", - "@vue/compiler-dom": "3.2.47", - "@vue/compiler-ssr": "3.2.47", - "@vue/reactivity-transform": "3.2.47", - "@vue/shared": "3.2.47", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.3.8.tgz", + "integrity": "sha512-WMzbUrlTjfYF8joyT84HfwwXo+8WPALuPxhy+BZ6R4Aafls+jDBnSz8PDz60uFhuqFbl3HxRfxvDzrUf3THwpA==", + "requires": { + "@babel/parser": "^7.23.0", + "@vue/compiler-core": "3.3.8", + "@vue/compiler-dom": "3.3.8", + "@vue/compiler-ssr": "3.3.8", + "@vue/reactivity-transform": "3.3.8", + "@vue/shared": "3.3.8", "estree-walker": "^2.0.2", - "magic-string": "^0.25.7", - "postcss": "^8.1.10", - "source-map": "^0.6.1" + "magic-string": "^0.30.5", + "postcss": "^8.4.31", + "source-map-js": "^1.0.2" } }, "@vue/compiler-ssr": { - "version": "3.2.47", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.47.tgz", - "integrity": "sha512-wVXC+gszhulcMD8wpxMsqSOpvDZ6xKXSVWkf50Guf/S+28hTAXPDYRTbLQ3EDkOP5Xz/+SY37YiwDquKbJOgZw==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.3.8.tgz", + "integrity": "sha512-hXCqQL/15kMVDBuoBYpUnSYT8doDNwsjvm3jTefnXr+ytn294ySnT8NlsFHmTgKNjwpuFy7XVV8yTeLtNl/P6w==", "requires": { - "@vue/compiler-dom": "3.2.47", - "@vue/shared": "3.2.47" + "@vue/compiler-dom": "3.3.8", + "@vue/shared": "3.3.8" } }, "@vue/devtools-api": { @@ -14048,57 +14035,57 @@ "integrity": "sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q==" }, "@vue/reactivity": { - "version": "3.2.47", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.47.tgz", - "integrity": "sha512-7khqQ/75oyyg+N/e+iwV6lpy1f5wq759NdlS1fpAhFXa8VeAIKGgk2E/C4VF59lx5b+Ezs5fpp/5WsRYXQiKxQ==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.3.8.tgz", + "integrity": "sha512-ctLWitmFBu6mtddPyOKpHg8+5ahouoTCRtmAHZAXmolDtuZXfjL2T3OJ6DL6ezBPQB1SmMnpzjiWjCiMYmpIuw==", "requires": { - "@vue/shared": "3.2.47" + "@vue/shared": "3.3.8" } }, "@vue/reactivity-transform": { - "version": "3.2.47", - "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.47.tgz", - "integrity": "sha512-m8lGXw8rdnPVVIdIFhf0LeQ/ixyHkH5plYuS83yop5n7ggVJU+z5v0zecwEnX7fa7HNLBhh2qngJJkxpwEEmYA==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.3.8.tgz", + "integrity": "sha512-49CvBzmZNtcHua0XJ7GdGifM8GOXoUMOX4dD40Y5DxI3R8OUhMlvf2nvgUAcPxaXiV5MQQ1Nwy09ADpnLQUqRw==", "requires": { - "@babel/parser": "^7.16.4", - "@vue/compiler-core": "3.2.47", - "@vue/shared": "3.2.47", + "@babel/parser": "^7.23.0", + "@vue/compiler-core": "3.3.8", + "@vue/shared": "3.3.8", "estree-walker": "^2.0.2", - "magic-string": "^0.25.7" + "magic-string": "^0.30.5" } }, "@vue/runtime-core": { - "version": "3.2.47", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.47.tgz", - "integrity": "sha512-RZxbLQIRB/K0ev0K9FXhNbBzT32H9iRtYbaXb0ZIz2usLms/D55dJR2t6cIEUn6vyhS3ALNvNthI+Q95C+NOpA==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.3.8.tgz", + "integrity": "sha512-qurzOlb6q26KWQ/8IShHkMDOuJkQnQcTIp1sdP4I9MbCf9FJeGVRXJFr2mF+6bXh/3Zjr9TDgURXrsCr9bfjUw==", "requires": { - "@vue/reactivity": "3.2.47", - "@vue/shared": "3.2.47" + "@vue/reactivity": "3.3.8", + "@vue/shared": "3.3.8" } }, "@vue/runtime-dom": { - "version": "3.2.47", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.47.tgz", - "integrity": "sha512-ArXrFTjS6TsDei4qwNvgrdmHtD930KgSKGhS5M+j8QxXrDJYLqYw4RRcDy1bz1m1wMmb6j+zGLifdVHtkXA7gA==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.3.8.tgz", + "integrity": "sha512-Noy5yM5UIf9UeFoowBVgghyGGPIDPy1Qlqt0yVsUdAVbqI8eeMSsTqBtauaEoT2UFXUk5S64aWVNJN4MJ2vRdA==", "requires": { - "@vue/runtime-core": "3.2.47", - "@vue/shared": "3.2.47", - "csstype": "^2.6.8" + "@vue/runtime-core": "3.3.8", + "@vue/shared": "3.3.8", + "csstype": "^3.1.2" } }, "@vue/server-renderer": { - "version": "3.2.47", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.47.tgz", - "integrity": "sha512-dN9gc1i8EvmP9RCzvneONXsKfBRgqFeFZLurmHOveL7oH6HiFXJw5OGu294n1nHc/HMgTy6LulU/tv5/A7f/LA==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.3.8.tgz", + "integrity": "sha512-zVCUw7RFskvPuNlPn/8xISbrf0zTWsTSdYTsUTN1ERGGZGVnRxM2QZ3x1OR32+vwkkCm0IW6HmJ49IsPm7ilLg==", "requires": { - "@vue/compiler-ssr": "3.2.47", - "@vue/shared": "3.2.47" + "@vue/compiler-ssr": "3.3.8", + "@vue/shared": "3.3.8" } }, "@vue/shared": { - "version": "3.2.47", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.47.tgz", - "integrity": "sha512-BHGyyGN3Q97EZx0taMQ+OLNuZcW3d37ZEVmEAyeoA9ERdGvm9Irc/0Fua8SNyOtV1w6BS4q25wbMzJujO9HIfQ==" + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.8.tgz", + "integrity": "sha512-8PGwybFwM4x8pcfgqEQFy70NaQxASvOC5DJwLQfpArw1UDfUXrJkdxD3BhVTMS+0Lef/TU7YO0Jvr0jJY8T+mw==" }, "@vue/test-utils": { "version": "2.4.1", @@ -15116,9 +15103,9 @@ } }, "csstype": { - "version": "2.6.21", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz", - "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==" + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" }, "cuint": { "version": "0.2.2", @@ -17372,11 +17359,18 @@ "integrity": "sha512-/M/R0gCDgM+Cv1IuBG1XGdfTFnMEG6PZeT+KGWHO/OG+imqmaD9CH5vHBTycEM3+Kc4uG2Il+tFAuUWLqQOeUA==" }, "magic-string": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", - "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", + "version": "0.30.5", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz", + "integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==", "requires": { - "sourcemap-codec": "^1.4.8" + "@jridgewell/sourcemap-codec": "^1.4.15" + }, + "dependencies": { + "@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" + } } }, "make-dir": { @@ -17523,9 +17517,9 @@ "integrity": "sha512-KpMNwdQsYz3O/SBS1qJ/o3sqUJ5wSb8gb0pul8CO0S56b9Y2ALm8zCfsjPXsqGFfoNBkDwZuZIAjhsZI03gYVQ==" }, "nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==" + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==" }, "natural-compare": { "version": "1.4.0", @@ -17816,18 +17810,18 @@ "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==" }, "pinia": { - "version": "2.0.32", - "resolved": "https://registry.npmjs.org/pinia/-/pinia-2.0.32.tgz", - "integrity": "sha512-8Tw4OrpCSJ028UUyp0gYPP/wyjigLoEceuO/x1G+FlHVf73337e5vLm4uDmrRIoBG1hvaed/eSHnrCFjOc4nkA==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/pinia/-/pinia-2.1.7.tgz", + "integrity": "sha512-+C2AHFtcFqjPih0zpYuvof37SFxMQ7OEG2zV9jRI12i9BOy3YQVAHwdKtyyc8pDcDyIc33WCIsZaCFWU7WWxGQ==", "requires": { "@vue/devtools-api": "^6.5.0", - "vue-demi": "*" + "vue-demi": ">=0.14.5" }, "dependencies": { "vue-demi": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.11.tgz", - "integrity": "sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==", + "version": "0.14.6", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.6.tgz", + "integrity": "sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==", "requires": {} } } @@ -17964,11 +17958,11 @@ "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==" }, "postcss": { - "version": "8.4.21", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz", - "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==", + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", "requires": { - "nanoid": "^3.3.4", + "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } @@ -18579,11 +18573,6 @@ "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==" }, - "sourcemap-codec": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==" - }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -19739,12 +19728,6 @@ "why-is-node-running": "^2.2.2" }, "dependencies": { - "@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true - }, "chai": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz", @@ -19768,28 +19751,19 @@ "requires": { "type-detect": "^4.0.0" } - }, - "magic-string": { - "version": "0.30.2", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.2.tgz", - "integrity": "sha512-lNZdu7pewtq/ZvWUp9Wpf/x7WzMTsR26TWV03BRZrXFsv+BI6dy8RAiKgm1uM/kyR0rCfUcqvOlXKG66KhIGug==", - "dev": true, - "requires": { - "@jridgewell/sourcemap-codec": "^1.4.15" - } } } }, "vue": { - "version": "3.2.47", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.2.47.tgz", - "integrity": "sha512-60188y/9Dc9WVrAZeUVSDxRQOZ+z+y5nO2ts9jWXSTkMvayiWxCWOWtBQoYjLeccfXkiiPZWAHcV+WTPhkqJHQ==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.3.8.tgz", + "integrity": "sha512-5VSX/3DabBikOXMsxzlW8JyfeLKlG9mzqnWgLQLty88vdZL7ZJgrdgBOmrArwxiLtmS+lNNpPcBYqrhE6TQW5w==", "requires": { - "@vue/compiler-dom": "3.2.47", - "@vue/compiler-sfc": "3.2.47", - "@vue/runtime-dom": "3.2.47", - "@vue/server-renderer": "3.2.47", - "@vue/shared": "3.2.47" + "@vue/compiler-dom": "3.3.8", + "@vue/compiler-sfc": "3.3.8", + "@vue/runtime-dom": "3.3.8", + "@vue/server-renderer": "3.3.8", + "@vue/shared": "3.3.8" } }, "vue-chartjs": { @@ -19852,9 +19826,9 @@ }, "dependencies": { "@vue/compiler-sfc": { - "version": "2.7.14", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-2.7.14.tgz", - "integrity": "sha512-aNmNHyLPsw+sVvlQFQ2/8sjNuLtK54TC6cuKnVzAY93ks4ZBrvwQSnkkIh7bsbNhum5hJBS00wSDipQ937f5DA==", + "version": "2.7.15", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-2.7.15.tgz", + "integrity": "sha512-FCvIEevPmgCgqFBH7wD+3B97y7u7oj/Wr69zADBf403Tui377bThTjBvekaZvlRr4IwUAu3M6hYZeULZFJbdYg==", "requires": { "@babel/parser": "^7.18.4", "postcss": "^8.4.14", @@ -19866,22 +19840,17 @@ "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==" }, - "csstype": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", - "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" - }, "luxon": { "version": "1.28.1", "resolved": "https://registry.npmjs.org/luxon/-/luxon-1.28.1.tgz", "integrity": "sha512-gYHAa180mKrNIUJCbwpmD0aTu9kV0dREDrwNnuyFAsO1Wt0EVYSZelPnJlbj9HplzXX/YWXHFTL45kvZ53M0pw==" }, "vue": { - "version": "2.7.14", - "resolved": "https://registry.npmjs.org/vue/-/vue-2.7.14.tgz", - "integrity": "sha512-b2qkFyOM0kwqWFuQmgd4o+uHGU7T+2z3T+WQp8UBjADfEv2n4FEMffzBmCKNP0IGzOEEfYjvtcC62xaSKeQDrQ==", + "version": "2.7.15", + "resolved": "https://registry.npmjs.org/vue/-/vue-2.7.15.tgz", + "integrity": "sha512-a29fsXd2G0KMRqIFTpRgpSbWaNBK3lpCTOLuGLEDnlHWdjB8fwl6zyYZ8xCrqkJdatwZb4mGHiEfJjnw0Q6AwQ==", "requires": { - "@vue/compiler-sfc": "2.7.14", + "@vue/compiler-sfc": "2.7.15", "csstype": "^3.1.0" } }, From 1d02d82d3ecdd212cb028c076a96f09cca50f604 Mon Sep 17 00:00:00 2001 From: Brock Anderson Date: Fri, 10 Nov 2023 10:30:33 -0800 Subject: [PATCH 20/49] added 'ref' attributes to some form fields so Vuetify components can be accessed from unit tests. also removed an unneeded state varaible --- frontend/src/components/InputForm.vue | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/frontend/src/components/InputForm.vue b/frontend/src/components/InputForm.vue index cb9799bc4..3eddd2a04 100644 --- a/frontend/src/components/InputForm.vue +++ b/frontend/src/components/InputForm.vue @@ -70,12 +70,14 @@ - + - + @@ -153,8 +155,7 @@ export default { companyAddress: '', naicsCode: null, naicsCodeList: ["2342"], - employeeCount: null, - employeeCountOptions: [], + employeeCountRange: null, isProcessing: false, uploadFileValue: null, minStartDate: moment().subtract(2, "years").format("yyyy-MM"), From 45e9a87bafa737abe4c9669139824de53266024e Mon Sep 17 00:00:00 2001 From: Brock Anderson Date: Fri, 10 Nov 2023 10:32:27 -0800 Subject: [PATCH 21/49] injected a 'test pinia' into the mounted component to allow testing of behaviour that depends on the 'code store'. added new unit tests for the 'employee count ranges' and 'naics code --- .../components/__tests__/InputForm.spec.ts | 75 ++++++++++++++----- 1 file changed, 57 insertions(+), 18 deletions(-) diff --git a/frontend/src/components/__tests__/InputForm.spec.ts b/frontend/src/components/__tests__/InputForm.spec.ts index 5945b90ba..b090bb569 100644 --- a/frontend/src/components/__tests__/InputForm.spec.ts +++ b/frontend/src/components/__tests__/InputForm.spec.ts @@ -1,27 +1,37 @@ -import { describe, it, expect, beforeEach, afterEach } from 'vitest' -import { mount } from '@vue/test-utils' -import { createVuetify } from "vuetify"; -import * as components from 'vuetify/components' -import * as directives from 'vuetify/directives' -import InputForm from '../InputForm.vue' +import { mount } from '@vue/test-utils'; import moment from 'moment'; +import { createTestingPinia } from '@pinia/testing'; +import { afterEach, beforeEach, describe, expect, it } from 'vitest'; +import { createVuetify } from "vuetify"; +import * as components from 'vuetify/components'; +import * as directives from 'vuetify/directives'; +import { useCodeStore } from '../../store/modules/codeStore'; +import InputForm from '../InputForm.vue'; + describe("InputForm", () => { - let wrapper = null; + let wrapper; + let pinia; beforeEach(() => { - //create an instance of vuetify so we can inject it into - //the mounted component, allowing it to behave as it would + //create an instances of vuetify and pinia so we can inject them + //into the mounted component, allowing it to behave as it would //in a browser const vuetify = createVuetify({ components, directives, }) + pinia = createTestingPinia({ + initialState: { + code: {}, + }, + }); + wrapper = mount(InputForm, { global: { - plugins: [vuetify], + plugins: [vuetify, pinia], } }) }) @@ -31,29 +41,58 @@ describe("InputForm", () => { wrapper.unmount(); } }) - - it('Renders with the expected form controls', () => { + + it('Renders with the expected form controls', () => { expect(wrapper.findAll("#companyName").length).toBe(1); expect(wrapper.findAll("#companyAddress").length).toBe(1); expect(wrapper.findAll("#naicsCode").length).toBe(1); - expect(wrapper.findAll("#employeeCount").length).toBe(1); + expect(wrapper.findAll("#employeeCountRange").length).toBe(1); expect(wrapper.findAll("#startDate").length).toBe(1); expect(wrapper.findAll("#endDate").length).toBe(1); expect(wrapper.findAll("#comments").length).toBe(1); expect(wrapper.find("#csvFile").attributes("type")).toBe("file"); expect(wrapper.find("#csvFile").attributes("accept")).toBe(".csv"); - + }) - it('Setting start date causes end date to default to one year later', async () => { + it("Form control for 'employee count ranges' is populated with the expected options", async () => { + + // Mock the employeeCountRanges property of the codeStore. InputForm.vue reads + // this property and uses it to populate the list of options for the Employee Count Range + // form field. + const codeStore = useCodeStore(pinia) + const employeeCountRanges = [{ "employee_count_range_id": 1, "employee_count_range": "10+" }]; + await codeStore.$patch({ employeeCountRanges: employeeCountRanges } as any) - const startDateComponent = wrapper.findComponent({ref: 'startDate'}) - const endDateComponent = wrapper.findComponent({ref: 'endDate'}) + const employeeCountRangeComponent = wrapper.findComponent({ ref: 'employeeCountRange' }) + expect(employeeCountRangeComponent.vm.items[0].employee_count_range).toBe(employeeCountRanges[0].employee_count_range); + expect(employeeCountRangeComponent.vm.items.length).toBe(employeeCountRanges.length); + + }) + + it("Form control for 'NAICS Code' is populated with the expected options", async () => { + + // Mock the naicsCodes property of the codeStore. InputForm.vue reads + // this property and uses it to populate the list of options for the NAICS Code + // form field. + const codeStore = useCodeStore(pinia) + const naicsCodes = ["1", "2"]; + await codeStore.$patch({ naicsCodes: naicsCodes } as any) + + const employeeCountRangeComponent = wrapper.findComponent({ ref: 'naicsCode' }) + expect(employeeCountRangeComponent.vm.items.length).toBe(naicsCodes.length); + + }) + + it('Setting start date causes end date to default to one year later', async () => { + + const startDateComponent = wrapper.findComponent({ ref: 'startDate' }) + const endDateComponent = wrapper.findComponent({ ref: 'endDate' }) const startDate = moment().subtract(1, "years").format("yyyy-MM"); const expectedEndDate = moment().subtract(1, "months").format("yyyy-MM"); await startDateComponent.setValue(startDate); - + expect(wrapper.vm.$data.startDate).toBe(startDate) expect(wrapper.vm.$data.endDate).toBe(expectedEndDate) From 96fbcd2f3c6bcb36e9f5bf2a6739a0c12436bcf7 Mon Sep 17 00:00:00 2001 From: Brock Anderson Date: Fri, 10 Nov 2023 17:11:07 -0800 Subject: [PATCH 22/49] bump vuetify version to 3.4.0 --- frontend/package-lock.json | 20 ++++++++++++-------- frontend/package.json | 2 +- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 34b0a4b3b..e9dc50dc8 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -43,7 +43,7 @@ "vue-meta": "^3.0.0-alpha.10", "vue-quick-chat": "^1.2.8", "vue-router": "^4.1.6", - "vuetify": "3.2.3" + "vuetify": "^3.4.0" }, "devDependencies": { "@babel/eslint-parser": "^7.19.1", @@ -11227,9 +11227,9 @@ } }, "node_modules/vuetify": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/vuetify/-/vuetify-3.2.3.tgz", - "integrity": "sha512-o7IJm/P5Ttp9ItF1ytQihsLzv4jxIYVfI4Ypkkqc4A7N2MeTmkDOPGbDNUgJ+G1p2upL00LCbc73A9YM8xYVpg==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/vuetify/-/vuetify-3.4.0.tgz", + "integrity": "sha512-aW3bJGCUN3fhl62yvsb+Hv6TtMWDqiadN0PTbEB8jd9z46/X1ddzQ/fhMjkqBX69sMFtZvENl3YFGU5c88/8qw==", "engines": { "node": "^12.20 || >=14.13" }, @@ -11238,12 +11238,16 @@ "url": "https://github.com/sponsors/johnleider" }, "peerDependencies": { + "typescript": ">=4.7", "vite-plugin-vuetify": "^1.0.0-alpha.12", - "vue": "^3.2.0", + "vue": "^3.3.0", "vue-i18n": "^9.0.0", "webpack-plugin-vuetify": "^2.0.0-alpha.11" }, "peerDependenciesMeta": { + "typescript": { + "optional": true + }, "vite-plugin-vuetify": { "optional": true }, @@ -19881,9 +19885,9 @@ } }, "vuetify": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/vuetify/-/vuetify-3.2.3.tgz", - "integrity": "sha512-o7IJm/P5Ttp9ItF1ytQihsLzv4jxIYVfI4Ypkkqc4A7N2MeTmkDOPGbDNUgJ+G1p2upL00LCbc73A9YM8xYVpg==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/vuetify/-/vuetify-3.4.0.tgz", + "integrity": "sha512-aW3bJGCUN3fhl62yvsb+Hv6TtMWDqiadN0PTbEB8jd9z46/X1ddzQ/fhMjkqBX69sMFtZvENl3YFGU5c88/8qw==", "requires": {} }, "w3c-xmlserializer": { diff --git a/frontend/package.json b/frontend/package.json index 01eb0e0be..268b62608 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -46,7 +46,7 @@ "vue-meta": "^3.0.0-alpha.10", "vue-quick-chat": "^1.2.8", "vue-router": "^4.1.6", - "vuetify": "3.2.3" + "vuetify": "^3.4.0" }, "devDependencies": { "@babel/eslint-parser": "^7.19.1", From 50d0e6518d4166c1e0440f94b0362825d31a230e Mon Sep 17 00:00:00 2001 From: Brock Anderson Date: Fri, 10 Nov 2023 17:11:52 -0800 Subject: [PATCH 23/49] add new function to post the file upload submission --- frontend/src/common/apiService.js | 12 ++++++++++++ frontend/src/utils/constant.js | 3 ++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/frontend/src/common/apiService.js b/frontend/src/common/apiService.js index 40997303c..0cc8e35a2 100644 --- a/frontend/src/common/apiService.js +++ b/frontend/src/common/apiService.js @@ -59,6 +59,18 @@ export default { delete apiAxios.defaults.headers.common['Authorization']; } }, + async postSubmission(formData) { + try{ + const resp = await apiAxios.post(ApiRoutes.POST_SUBMISSION, formData); + if (resp?.data) { + return resp.data; + } + throw new Error("Unable to post the submission"); + } catch(e) { + console.log(`Failed topost the submission - ${e}`); + throw e; + } + }, async getEmployeeCountRanges() { try{ const resp = await apiAxios.get(ApiRoutes.EMPLOYEE_COUNT_RANGES); diff --git a/frontend/src/utils/constant.js b/frontend/src/utils/constant.js index bb4383013..4af0c7c18 100644 --- a/frontend/src/utils/constant.js +++ b/frontend/src/utils/constant.js @@ -24,7 +24,8 @@ export const ApiRoutes = Object.freeze({ fileUpload: { BASE_URL: fileUploadRoot, }, - EMPLOYEE_COUNT_RANGES: baseRoot + "/v1/codes/employee-count-ranges" + EMPLOYEE_COUNT_RANGES: baseRoot + "/v1/codes/employee-count-ranges", + POST_SUBMISSION: baseRoot + "/v1/file-upload", }); export const PAGE_TITLES = Object.freeze({ From 34217b3b3e5e036a7048fc8802b7f11ea5890bc7 Mon Sep 17 00:00:00 2001 From: Brock Anderson Date: Fri, 10 Nov 2023 17:12:15 -0800 Subject: [PATCH 24/49] added form validation. --- frontend/src/components/InputForm.vue | 78 +++++++++++++++++++-------- 1 file changed, 57 insertions(+), 21 deletions(-) diff --git a/frontend/src/components/InputForm.vue b/frontend/src/components/InputForm.vue index 3eddd2a04..5a99df5c1 100644 --- a/frontend/src/components/InputForm.vue +++ b/frontend/src/components/InputForm.vue @@ -1,10 +1,8 @@ -