From e89c4c3666cd4e24efadbf7fc23e2139e88f68c2 Mon Sep 17 00:00:00 2001 From: Troy Washington <103076891+TVW96@users.noreply.github.com> Date: Fri, 6 Mar 2026 20:46:00 -0800 Subject: [PATCH 1/6] add event seeding file for populating home page with test events --- package-lock.json | 77 ++++++++++------- package.json | 3 +- scripts/seed-events.js | 192 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 241 insertions(+), 31 deletions(-) create mode 100644 scripts/seed-events.js diff --git a/package-lock.json b/package-lock.json index 92df205..4c32fe9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1246,6 +1246,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "dev": true, + "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -1842,6 +1843,7 @@ "url": "https://opencollective.com/csstools" } ], + "peer": true, "engines": { "node": ">=18" }, @@ -1883,6 +1885,7 @@ "url": "https://opencollective.com/csstools" } ], + "peer": true, "engines": { "node": ">=18" } @@ -2016,6 +2019,7 @@ "version": "11.13.1", "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.13.1.tgz", "integrity": "sha512-iqouYkuEblRcXmylXIwwOodiEK5Ifl7JcX7o6V4jI3iW4mLXX3dmt5xwBtIkJiQEXFAI+pC8X0i67yiPkH9Ucw==", + "peer": true, "dependencies": { "@emotion/memoize": "^0.9.0", "@emotion/sheet": "^1.4.0", @@ -2046,6 +2050,7 @@ "version": "11.14.0", "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", + "peer": true, "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", @@ -2098,6 +2103,7 @@ "version": "11.14.1", "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.1.tgz", "integrity": "sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw==", + "peer": true, "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", @@ -3561,6 +3567,7 @@ "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", "dev": true, + "peer": true, "dependencies": { "@babel/core": "^7.11.6", "@jest/types": "^29.6.3", @@ -3593,6 +3600,7 @@ "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", "dev": true, + "peer": true, "dependencies": { "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", @@ -3738,6 +3746,7 @@ "version": "5.18.0", "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.18.0.tgz", "integrity": "sha512-bbH/HaJZpFtXGvWg3TsBWG4eyt3gah3E7nCNU8GLyRjVoWcA91Vm/T+sjHfUcwgJSw9iLtucfHBoq+qW/T30aA==", + "peer": true, "dependencies": { "@babel/runtime": "^7.23.9", "@mui/core-downloads-tracker": "^5.18.0", @@ -4318,6 +4327,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", "dev": true, + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -4376,6 +4386,7 @@ "version": "10.4.20", "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-10.4.20.tgz", "integrity": "sha512-hxJxZF7jcKGuUzM9EYbuES80Z/36piJbiqmPy86mk8qOn5gglFebBTvcx7PWVbRNSb4gngASYnefBj/Y2HAzpQ==", + "peer": true, "dependencies": { "file-type": "20.4.1", "iterare": "1.2.1", @@ -4431,6 +4442,7 @@ "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-10.4.20.tgz", "integrity": "sha512-kRdtyKA3+Tu70N3RQ4JgmO1E3LzAMs/eppj7SfjabC7TgqNWoS4RLhWl4BqmsNVmjj6D5jgfPVtHtgYkU3AfpQ==", "hasInstallScript": true, + "peer": true, "dependencies": { "@nuxtjs/opencollective": "0.3.2", "fast-safe-stringify": "2.1.1", @@ -4512,6 +4524,7 @@ "version": "10.4.20", "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-10.4.20.tgz", "integrity": "sha512-rh97mX3rimyf4xLMLHuTOBKe6UD8LOJ14VlJ1F/PTd6C6ZK9Ak6EHuJvdaGcSFQhd3ZMBh3I6CuujKGW9pNdIg==", + "peer": true, "dependencies": { "body-parser": "1.20.3", "cors": "2.8.5", @@ -5205,6 +5218,7 @@ "integrity": "sha512-akea+6bHYBBfA9uQqSYmlJXn61cTa+jbO87xVLCWbTqbWadRVmhxlXATaOjOgcBaWU4ePo0wB41KMFv3o35IXA==", "devOptional": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "playwright": "1.58.2" }, @@ -6453,6 +6467,7 @@ "version": "22.14.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.1.tgz", "integrity": "sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw==", + "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -6533,6 +6548,7 @@ "version": "18.3.12", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.12.tgz", "integrity": "sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==", + "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -6716,6 +6732,7 @@ "version": "5.59.11", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.11.tgz", "integrity": "sha512-s9ZF3M+Nym6CAZEkJJeO2TFHHDsKAM3ecNkLuH4i4s8/RCPnF5JRip2GyviYkeEAcwGMJxkqG9h2dAsnA1nZpA==", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "5.59.11", "@typescript-eslint/types": "5.59.11", @@ -7271,7 +7288,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", - "peer": true, "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" @@ -7284,6 +7300,7 @@ "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -7296,7 +7313,6 @@ "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", "dev": true, - "peer": true, "engines": { "node": ">=10.13.0" }, @@ -7340,6 +7356,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "dev": true, + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -7788,6 +7805,7 @@ "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", "dev": true, + "peer": true, "dependencies": { "@jest/transform": "^29.7.0", "@types/babel__core": "^7.1.14", @@ -8107,6 +8125,7 @@ "url": "https://github.com/sponsors/ai" } ], + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.25", "caniuse-lite": "^1.0.30001754", @@ -8376,12 +8395,14 @@ "node_modules/class-transformer": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", - "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==" + "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==", + "peer": true }, "node_modules/class-validator": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.0.tgz", "integrity": "sha512-ct3ltplN8I9fOwUd8GrP8UQixwff129BkEtuWDKL5W45cQuLd19xqmTLu5ge78YDm/fdje6FMt0hGOhl0lii3A==", + "peer": true, "dependencies": { "@types/validator": "^13.7.10", "libphonenumber-js": "^1.10.14", @@ -8806,7 +8827,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", - "peer": true, "dependencies": { "safe-buffer": "5.2.1" }, @@ -8839,7 +8859,6 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", - "peer": true, "engines": { "node": ">=6.6.0" } @@ -9085,6 +9104,7 @@ "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", "devOptional": true, + "peer": true, "dependencies": { "@babel/runtime": "^7.21.0" }, @@ -9099,7 +9119,8 @@ "node_modules/dayjs": { "version": "1.11.19", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz", - "integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==" + "integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==", + "peer": true }, "node_modules/debug": { "version": "4.4.3", @@ -9708,6 +9729,7 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.42.0.tgz", "integrity": "sha512-ulg9Ms6E1WPf67PHaEY4/6E2tEn5/f7FXGzr3t9cBMugOmf1INYvuUwwh1aXQN4MfJ6a5K2iNwP3w4AColvI9A==", "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.4.0", @@ -9876,6 +9898,7 @@ "version": "2.32.0", "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", + "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -10571,7 +10594,6 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", - "peer": true, "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.0", @@ -10613,7 +10635,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", - "peer": true, "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", @@ -10633,7 +10654,6 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "peer": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -10645,7 +10665,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", - "peer": true, "engines": { "node": ">= 0.8" } @@ -10654,7 +10673,6 @@ "version": "6.14.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", - "peer": true, "dependencies": { "side-channel": "^1.1.0" }, @@ -10669,7 +10687,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.1.tgz", "integrity": "sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==", - "peer": true, "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", @@ -10684,7 +10701,6 @@ "version": "0.7.0", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", - "peer": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -10700,7 +10716,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", - "peer": true, "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", @@ -10881,7 +10896,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", - "peer": true, "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", @@ -11134,7 +11148,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", - "peer": true, "engines": { "node": ">= 0.8" } @@ -12470,8 +12483,7 @@ "node_modules/is-promise": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", - "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", - "peer": true + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==" }, "node_modules/is-regex": { "version": "1.2.1", @@ -12766,6 +12778,7 @@ "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, + "peer": true, "dependencies": { "@jest/core": "^29.7.0", "@jest/types": "^29.6.3", @@ -13984,6 +13997,7 @@ "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-27.2.0.tgz", "integrity": "sha512-454TI39PeRDW1LgpyLPyURtB4Zx1tklSr6+OFOipsxGUH1WMTvk6C65JQdrj455+DP2uJ1+veBEHTGFKWVLFoA==", "dev": true, + "peer": true, "dependencies": { "@acemir/cssom": "^0.9.23", "@asamuzakjp/dom-selector": "^6.7.4", @@ -14635,7 +14649,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", - "peer": true, "engines": { "node": ">=18" }, @@ -14693,7 +14706,6 @@ "version": "1.54.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", - "peer": true, "engines": { "node": ">= 0.6" } @@ -14702,7 +14714,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", - "peer": true, "dependencies": { "mime-db": "^1.54.0" }, @@ -14899,7 +14910,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", - "peer": true, "engines": { "node": ">= 0.6" } @@ -14926,6 +14936,7 @@ "version": "14.2.33", "resolved": "https://registry.npmjs.org/next/-/next-14.2.33.tgz", "integrity": "sha512-GiKHLsD00t4ACm1p00VgrI0rUFAC9cRDGReKyERlM57aeEZkOQGcZTpIbsGn0b562FTPJWmYfKwplfO9EaT6ng==", + "peer": true, "dependencies": { "@next/env": "14.2.33", "@swc/helpers": "0.5.5", @@ -15621,6 +15632,7 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/passport/-/passport-0.6.0.tgz", "integrity": "sha512-0fe+p3ZnrWRW74fe8+SvCyf4a3Pb2/h7gFkQ8yTJpAO50gDzlfjZUZTO1k5Eg9kUct22OxHLqDZoKUWRHOh9ug==", + "peer": true, "dependencies": { "passport-strategy": "1.x.x", "pause": "0.0.1", @@ -15728,6 +15740,7 @@ "version": "8.16.3", "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz", "integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==", + "peer": true, "dependencies": { "pg-connection-string": "^2.9.1", "pg-pool": "^3.10.1", @@ -15995,6 +16008,7 @@ "url": "https://github.com/sponsors/ai" } ], + "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -16169,6 +16183,7 @@ "version": "10.27.2", "resolved": "https://registry.npmjs.org/preact/-/preact-10.27.2.tgz", "integrity": "sha512-5SYSgFKSyhCbk6SrXyMpqjb5+MQBgfvEKE/OC+PujcY34sOpqtr+0AZQtPYx5IA6VxynQ7rUPCtKzyovpj9Bpg==", + "peer": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/preact" @@ -16203,6 +16218,7 @@ "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", "dev": true, + "peer": true, "bin": { "prettier": "bin-prettier.js" }, @@ -16403,6 +16419,7 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -16463,6 +16480,7 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -16865,7 +16883,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", - "peer": true, "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", @@ -16881,7 +16898,6 @@ "version": "8.3.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", - "peer": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/express" @@ -16922,6 +16938,7 @@ "version": "7.8.2", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "peer": true, "dependencies": { "tslib": "^2.1.0" } @@ -17050,6 +17067,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -17091,7 +17109,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", - "peer": true, "dependencies": { "debug": "^4.3.5", "encodeurl": "^2.0.0", @@ -17122,7 +17139,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", - "peer": true, "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", @@ -17541,7 +17557,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", - "peer": true, "engines": { "node": ">= 0.8" } @@ -18357,6 +18372,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "peer": true, "engines": { "node": ">=12" }, @@ -18552,6 +18568,7 @@ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", "devOptional": true, + "peer": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -18799,6 +18816,7 @@ "version": "0.3.27", "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.27.tgz", "integrity": "sha512-pNV1bn+1n8qEe8tUNsNdD8ejuPcMAg47u2lUGnbsajiNUr3p2Js1XLKQjBMH0yMRMDfdX8T+fIRejFmIwy9x4A==", + "peer": true, "dependencies": { "@sqltools/formatter": "^1.2.5", "ansis": "^3.17.0", @@ -18900,6 +18918,7 @@ "version": "5.6.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -19249,7 +19268,6 @@ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true, - "peer": true, "engines": { "node": ">= 0.6" } @@ -19259,7 +19277,6 @@ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, - "peer": true, "dependencies": { "mime-db": "1.52.0" }, @@ -19272,7 +19289,6 @@ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", "dev": true, - "peer": true, "dependencies": { "@types/json-schema": "^7.0.9", "ajv": "^8.9.0", @@ -19476,6 +19492,7 @@ "version": "3.18.3", "resolved": "https://registry.npmjs.org/winston/-/winston-3.18.3.tgz", "integrity": "sha512-NoBZauFNNWENgsnC9YpgyYwOVrl2m58PpQ8lNHjV3kosGs7KJ7Npk9pCUE+WJlawVSe8mykWDKWFSVfs3QO9ww==", + "peer": true, "dependencies": { "@colors/colors": "^1.6.0", "@dabh/diagnostics": "^2.0.8", diff --git a/package.json b/package.json index 8a5a3b8..ede7a46 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,8 @@ "test:e2e:ui": "playwright test --ui", "test:e2e:debug": "playwright test --debug", "test:e2e:codegen": "playwright codegen http://localhost:8080", - "test:all": "npm run test && npm run test:e2e" + "test:all": "npm run test && npm run test:e2e", + "seed": "node scripts/seed-events.js" }, "devDependencies": { "@playwright/test": "^1.57.0", diff --git a/scripts/seed-events.js b/scripts/seed-events.js new file mode 100644 index 0000000..a032ab1 --- /dev/null +++ b/scripts/seed-events.js @@ -0,0 +1,192 @@ +/** + * Seed script — populates the local database with sample events. + * + * Usage (from repo root): + * npm run seed + * + * Requires the NestJS backend to be running on port 3000. + * A seed creator account is created (or reused if it already exists). + */ + +// eslint-disable-next-line @typescript-eslint/no-var-requires +const axios = require("axios"); + +const API = process.env.PLAYWRIGHT_API_URL || "http://localhost:3000/api"; + +const SEED_USER = { + email: "seed-creator@nsc.dev", + password: "Seed@Creator123", + firstName: "Seed", + lastName: "Creator", + pronouns: "they/them", + role: "creator", +}; + +// ─── Event templates ────────────────────────────────────────────────────────── + +const today = new Date(); +function d(daysOffset, hour = 9) { + const dt = new Date(today); + dt.setDate(dt.getDate() + daysOffset); + dt.setHours(hour, 0, 0, 0); + return dt.toISOString(); +} + +const EVENTS = [ + { + eventTitle: "Web Dev Workshop: Next.js 15 Deep Dive", + eventDescription: + "An in-depth hands-on workshop covering the latest features in Next.js 15, including server actions, partial pre-rendering, and the new caching model.", + startDate: d(3, 10), + endDate: d(3, 13), + eventLocation: "Building A, Room 201", + eventHost: "NSC Web Dev Club", + eventCapacity: "40", + eventContact: "webdev@nsc.dev", + tagNames: ["Tech", "Workshop", "Web"], + eventPrivacy: "Public", + eventSchedule: "10:00 AM – Intro\n11:00 AM – Hands-on Lab\n12:00 PM – Q&A", + eventSpeakers: ["Jane Smith"], + isHidden: false, + isArchived: false, + }, + { + eventTitle: "Cloud & AWS Study Group – S3 & IAM", + eventDescription: + "Study group focused on AWS fundamentals: S3 bucket policies, IAM roles, and access points. Bring your laptop.", + startDate: d(5, 14), + endDate: d(5, 16), + eventLocation: "Library, Study Room 4", + eventHost: "NSC Cloud Club", + eventCapacity: "20", + eventContact: "cloud@nsc.dev", + tagNames: ["AWS", "Cloud", "Study Group"], + eventPrivacy: "Public", + isHidden: false, + isArchived: false, + }, + { + eventTitle: "Spring Career Fair 2026", + eventDescription: + "Meet recruiters from 30+ tech companies. Bring printed résumés. Business casual dress required.", + startDate: d(10, 9), + endDate: d(10, 17), + eventLocation: "Main Campus Gymnasium", + eventHost: "NSC Career Services", + eventCapacity: "300", + eventContact: "careers@nsc.dev", + tagNames: ["Career", "Networking"], + eventPrivacy: "Public", + isHidden: false, + isArchived: false, + }, + { + eventTitle: "Hackathon: Build for Good", + eventDescription: + "24-hour hackathon with prizes. Build a project that addresses a real community need. Teams of 2–4.", + startDate: d(14, 8), + endDate: d(15, 8), + eventLocation: "Innovation Hub, Floor 3", + eventHost: "NSC ACM Chapter", + eventCapacity: "80", + eventContact: "acm@nsc.dev", + tagNames: ["Hackathon", "Tech", "Community"], + eventPrivacy: "Public", + eventSchedule: + "Day 1 08:00 – Kickoff\nDay 2 08:00 – Presentations\nDay 2 10:00 – Awards", + isHidden: false, + isArchived: false, + }, + { + eventTitle: "Intro to Machine Learning", + eventDescription: + "Beginner-friendly seminar covering supervised vs. unsupervised learning, common algorithms, and a live Python demo.", + startDate: d(7, 15), + endDate: d(7, 17), + eventLocation: "Science Hall, Room 110", + eventHost: "NSC AI Club", + eventCapacity: "60", + eventContact: "ai@nsc.dev", + tagNames: ["AI", "Python", "Workshop"], + eventPrivacy: "Public", + eventSpeakers: ["Dr. Alice Chen", "Bob Tanaka"], + isHidden: false, + isArchived: false, + }, + { + eventTitle: "Open Source Contribution Day", + eventDescription: + "Find a project, open a PR, get mentored. All skill levels welcome. Coffee and snacks provided.", + startDate: d(21, 10), + endDate: d(21, 14), + eventLocation: "CS Building, Open Lab", + eventHost: "NSC Open Source Club", + eventCapacity: "50", + eventContact: "opensource@nsc.dev", + tagNames: ["Open Source", "Git", "Community"], + eventPrivacy: "Public", + isHidden: false, + isArchived: false, + }, +]; + +// ─── Helpers ────────────────────────────────────────────────────────────────── + +async function getToken() { + // Try existing seed account first + try { + const res = await axios.post(`${API}/auth/login`, { + email: SEED_USER.email, + password: SEED_USER.password, + }); + const token = res.data?.token || res.data?.data?.token; + if (token) { + console.log("✓ Logged in as existing seed user"); + return token; + } + } catch { + // User doesn't exist yet — sign up below + } + + const res = await axios.post(`${API}/auth/signup`, SEED_USER); + const token = res.data?.token || res.data?.data?.token; + if (!token) throw new Error("Signup succeeded but no token returned"); + console.log("✓ Created seed user and logged in"); + return token; +} + +// ─── Main ───────────────────────────────────────────────────────────────────── + +async function seed() { + console.log(`\nSeeding events against ${API}\n`); + + let token; + try { + token = await getToken(); + } catch (err) { + console.error("✗ Auth failed:", err.response?.data ?? err.message); + process.exit(1); + } + + const client = axios.create({ + baseURL: API, + headers: { Authorization: `Bearer ${token}` }, + }); + + let created = 0; + for (const event of EVENTS) { + try { + const res = await client.post("/events/new", event); + const id = res.data?.id || res.data?.data?.id; + console.log(` ✓ "${event.eventTitle}" (id: ${id})`); + created++; + } catch (err) { + const msg = err.response?.data?.message ?? err.message; + console.error(` ✗ "${event.eventTitle}" — ${JSON.stringify(msg)}`); + } + } + + console.log(`\nDone — ${created}/${EVENTS.length} events created.\n`); +} + +seed(); From 8c4ee901a41d877dd94028773ee7b586c5450bf6 Mon Sep 17 00:00:00 2001 From: Troy Washington <103076891+TVW96@users.noreply.github.com> Date: Fri, 6 Mar 2026 21:23:54 -0800 Subject: [PATCH 2/6] change name of seed script --- package.json | 2 +- scripts/{seed-events.js => events-seed.js} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename scripts/{seed-events.js => events-seed.js} (100%) diff --git a/package.json b/package.json index ede7a46..064284f 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "test:e2e:debug": "playwright test --debug", "test:e2e:codegen": "playwright codegen http://localhost:8080", "test:all": "npm run test && npm run test:e2e", - "seed": "node scripts/seed-events.js" + "seed": "node scripts/events-seed.js" }, "devDependencies": { "@playwright/test": "^1.57.0", diff --git a/scripts/seed-events.js b/scripts/events-seed.js similarity index 100% rename from scripts/seed-events.js rename to scripts/events-seed.js From 69c16cecff90d2179406f0c2d638dadec39b6e19 Mon Sep 17 00:00:00 2001 From: Troy Washington <103076891+TVW96@users.noreply.github.com> Date: Sat, 7 Mar 2026 16:25:53 -0800 Subject: [PATCH 3/6] add seed file for creator, admin, user accounts --- package.json | 3 +- scripts/users-seed.js | 91 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 scripts/users-seed.js diff --git a/package.json b/package.json index 064284f..f5338a6 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,8 @@ "test:e2e:debug": "playwright test --debug", "test:e2e:codegen": "playwright codegen http://localhost:8080", "test:all": "npm run test && npm run test:e2e", - "seed": "node scripts/events-seed.js" + "seed": "node scripts/events-seed.js", + "seed:users": "node scripts/users-seed.js" }, "devDependencies": { "@playwright/test": "^1.57.0", diff --git a/scripts/users-seed.js b/scripts/users-seed.js new file mode 100644 index 0000000..6041fc2 --- /dev/null +++ b/scripts/users-seed.js @@ -0,0 +1,91 @@ +/** + * Seed script — populates the local database with sample user profiles. + * + * Usage (from repo root): + * npm run seed:users + * + * Requires the NestJS backend to be running on port 3000. + * Each user is registered via POST /auth/signup. Already-existing accounts + * (HTTP 409) are skipped rather than treated as errors. + */ + +// eslint-disable-next-line @typescript-eslint/no-var-requires +const axios = require("axios"); + +const API = process.env.PLAYWRIGHT_API_URL || "http://localhost:3000/api"; + +// ─── User templates ────────────────────────────────────────────────────────── + +const SEED_USERS = [ + { + email: "seed-creator@nsc.dev", + password: "Seed@Creator123", + firstName: "Seed", + lastName: "Creator", + pronouns: "they/them", + role: "creator", + }, + { + email: "admin@nsc.dev", + password: "admin@admin123", + firstName: "Admin", + lastName: "Account", + pronouns: "they/them", + role: "admin", + }, + { + email: "user@nsc.dev", + password: "user@user123", + firstName: "User", + lastName: "Account", + pronouns: "they/them", + role: "user", + }, + { + email: "user2@nsc.dev", + password: "user2@user123", + firstName: "User", + lastName: "Two", + pronouns: "she/her", + role: "user", + }, + { + email: "creator2@nsc.dev", + password: "creator2@creator123", + firstName: "Creator", + lastName: "Two", + pronouns: "he/him", + role: "creator", + } +]; + +// ─── Main ───────────────────────────────────────────────────────────────────── + +async function seed() { + console.log(`\nSeeding users against ${API}\n`); + + let created = 0; + let skipped = 0; + + for (const user of SEED_USERS) { + try { + await axios.post(`${API}/auth/signup`, user); + console.log(` ✓ Created "${user.email}" (role: ${user.role})`); + created++; + } catch (err) { + if (err.response?.status === 409) { + console.log(` ~ Skipped "${user.email}" — already exists`); + skipped++; + } else { + const msg = err.response?.data?.message ?? err.message; + console.error(` ✗ "${user.email}" — ${JSON.stringify(msg)}`); + } + } + } + + console.log( + `\nDone — ${created} created, ${skipped} skipped, ${SEED_USERS.length - created - skipped} failed.\n` + ); +} + +seed(); From 7817cf23695fbb55c4444e00b620e798c8bb52b4 Mon Sep 17 00:00:00 2001 From: Troy Washington <103076891+TVW96@users.noreply.github.com> Date: Sat, 7 Mar 2026 16:41:40 -0800 Subject: [PATCH 4/6] patch update user profiles if already existing --- scripts/users-seed.js | 121 +++++++++++++++++++++++++++++++++++------- 1 file changed, 101 insertions(+), 20 deletions(-) diff --git a/scripts/users-seed.js b/scripts/users-seed.js index 6041fc2..431ff0c 100644 --- a/scripts/users-seed.js +++ b/scripts/users-seed.js @@ -5,8 +5,9 @@ * npm run seed:users * * Requires the NestJS backend to be running on port 3000. - * Each user is registered via POST /auth/signup. Already-existing accounts - * (HTTP 409) are skipped rather than treated as errors. + * New accounts are created via POST /auth/signup. If the email is already in + * use (HTTP 409) the existing account is updated via PATCH /users/update/:id + * using an admin token. */ // eslint-disable-next-line @typescript-eslint/no-var-requires @@ -14,6 +15,9 @@ const axios = require("axios"); const API = process.env.PLAYWRIGHT_API_URL || "http://localhost:3000/api"; +// The admin entry in SEED_USERS is used to obtain a token for update calls. +const ADMIN = { email: "admin@nsc.dev", password: "admin@admin123" }; + // ─── User templates ────────────────────────────────────────────────────────── const SEED_USERS = [ @@ -29,7 +33,15 @@ const SEED_USERS = [ email: "admin@nsc.dev", password: "admin@admin123", firstName: "Admin", - lastName: "Account", + lastName: "TestAccount", + pronouns: "they/them", + role: "admin", + }, + { + email: "admin2@nsc.dev", + password: "admin@admin123", + firstName: "Admin2", + lastName: "TestAccount", pronouns: "they/them", role: "admin", }, @@ -37,54 +49,123 @@ const SEED_USERS = [ email: "user@nsc.dev", password: "user@user123", firstName: "User", - lastName: "Account", + lastName: "TestAccount", pronouns: "they/them", role: "user", }, { email: "user2@nsc.dev", - password: "user2@user123", + password: "user@user123", firstName: "User", - lastName: "Two", + lastName: "TestAccount", pronouns: "she/her", role: "user", }, { - email: "creator2@nsc.dev", - password: "creator2@creator123", - firstName: "Creator", - lastName: "Two", - pronouns: "he/him", - role: "creator", - } + email: "user3@nsc.dev", + password: "user@user123", + firstName: "User3", + lastName: "TestAccount", + pronouns: "they/them", + role: "user", + }, ]; +// ─── Helpers ────────────────────────────────────────────────────────────────── + +async function getAdminToken() { + // Try logging in first + try { + const res = await axios.post(`${API}/auth/login`, ADMIN); + const token = res.data?.token || res.data?.data?.token; + if (token) return token; + } catch { + // Admin doesn't exist yet — will be created in the main loop + } + return null; +} + // ─── Main ───────────────────────────────────────────────────────────────────── async function seed() { console.log(`\nSeeding users against ${API}\n`); + // Ensure admin exists before processing the full list so we have a token + // for any update calls that follow. + for (const user of SEED_USERS) { + if (user.email === ADMIN.email) { + try { + await axios.post(`${API}/auth/signup`, user); + console.log(` ✓ Created "${user.email}" (role: ${user.role})`); + } catch { + // Signup failed — admin may already exist; getAdminToken() will login below. + console.log( + ` ~ Admin "${user.email}" already exists, skipping signup`, + ); + } + break; + } + } + + const adminToken = await getAdminToken(); + if (!adminToken) { + console.error(" ✗ Could not obtain admin token — aborting."); + process.exit(1); + } + + const client = axios.create({ + baseURL: API, + headers: { Authorization: `Bearer ${adminToken}` }, + }); + let created = 0; - let skipped = 0; + let updated = 0; + let failed = 0; for (const user of SEED_USERS) { + if (user.email === ADMIN.email) continue; // already handled above + try { await axios.post(`${API}/auth/signup`, user); console.log(` ✓ Created "${user.email}" (role: ${user.role})`); created++; } catch (err) { - if (err.response?.status === 409) { - console.log(` ~ Skipped "${user.email}" — already exists`); - skipped++; + const status = err.response?.status; + const message = err.response?.data?.message ?? err.message ?? ""; + const alreadyExists = + status === 409 || message.toLowerCase().includes("already exists"); + + if (alreadyExists) { + // User exists — look up by email then patch + try { + const found = await client.get( + `/users/email/${encodeURIComponent(user.email)}`, + ); + const id = found.data?.id || found.data?.data?.id; + await client.patch(`/users/update/${id}`, { + firstName: user.firstName, + lastName: user.lastName, + pronouns: user.pronouns, + role: user.role, + }); + console.log(` ↻ Updated "${user.email}" (role: ${user.role})`); + updated++; + } catch (updateErr) { + const msg = updateErr.response?.data?.message ?? updateErr.message; + console.error( + ` ✗ "${user.email}" update failed — ${JSON.stringify(msg)}`, + ); + failed++; + } } else { - const msg = err.response?.data?.message ?? err.message; - console.error(` ✗ "${user.email}" — ${JSON.stringify(msg)}`); + console.error(` ✗ "${user.email}" — ${JSON.stringify(message)}`); + failed++; } } } console.log( - `\nDone — ${created} created, ${skipped} skipped, ${SEED_USERS.length - created - skipped} failed.\n` + `\nDone — ${created} created, ${updated} updated, ${failed} failed.\n`, ); } From 449c5fa50c223552cf4d45a7d13110ca063875fb Mon Sep 17 00:00:00 2001 From: Troy Washington <103076891+TVW96@users.noreply.github.com> Date: Sat, 7 Mar 2026 17:07:21 -0800 Subject: [PATCH 5/6] added: admin user to test files, test user removal afterAll test completed, global-teardown logic (fetch test user & event data for deletion) --- e2e/fixtures/auth.ts | 70 +++++++++++++--- e2e/tests/event-management.spec.ts | 19 +++++ e2e/tests/event-registration.spec.ts | 42 ++++++++++ e2e/tests/user-management.spec.ts | 39 +++++++++ e2e/utils/api-client.ts | 25 +++++- e2e/utils/global-teardown.ts | 118 ++++++++++++++++++++++++++- playwright.config.ts | 58 ++++++------- 7 files changed, 326 insertions(+), 45 deletions(-) diff --git a/e2e/fixtures/auth.ts b/e2e/fixtures/auth.ts index 4bd0d79..a964f5a 100644 --- a/e2e/fixtures/auth.ts +++ b/e2e/fixtures/auth.ts @@ -1,6 +1,30 @@ import { test as base, expect } from '@playwright/test'; import { ApiClient } from '../utils/api-client'; +const ADMIN_API_URL = + process.env.PLAYWRIGHT_API_URL || "http://localhost:3000/api"; +const ADMIN_CREDENTIALS = { + email: "admin@nsc.dev", + password: "admin@admin123", +}; + +/** Returns an ApiClient authenticated as the seed admin. Best-effort — returns null if login fails. */ +async function getAdminClient(): Promise { + try { + const adminClient = new ApiClient(ADMIN_API_URL); + const res = await adminClient.login( + ADMIN_CREDENTIALS.email, + ADMIN_CREDENTIALS.password, + ); + const token = res.data?.token || res.data?.data?.token; + if (!token) return null; + adminClient.setToken(token); + return adminClient; + } catch { + return null; + } +} + type AuthFixtures = { authenticatedPage: { page: any; @@ -36,7 +60,10 @@ export const test = base.extend({ ); // Login to get token - const loginResponse = await apiClient.login(testUser.email, testUser.password); + const loginResponse = await apiClient.login( + testUser.email, + testUser.password, + ); const token = loginResponse.data.access_token; apiClient.setToken(token); @@ -44,23 +71,26 @@ export const test = base.extend({ // Set authentication context in page await page.context().addCookies([ { - name: 'next-auth.session-token', + name: "next-auth.session-token", value: token, - domain: 'localhost', - path: '/', + domain: "localhost", + path: "/", httpOnly: true, secure: false, - sameSite: 'Lax', + sameSite: "Lax", }, ]); // Store token in localStorage - await page.goto('/'); - await page.evaluate(({ tokenValue }) => { - if (tokenValue) { - localStorage.setItem('authToken', tokenValue); - } - }, { tokenValue: token }); + await page.goto("/"); + await page.evaluate( + ({ tokenValue }) => { + if (tokenValue) { + localStorage.setItem("authToken", tokenValue); + } + }, + { tokenValue: token }, + ); const user = signupResponse.data.user || loginResponse.data.user; @@ -69,6 +99,24 @@ export const test = base.extend({ apiClient, user, }); + + // ── Cleanup: delete the test user via admin account ────────────────── + try { + const adminClient = await getAdminClient(); + if (adminClient) { + // Resolve user ID (may come from signup or login response) + let userId: string | undefined = user?.id; + if (!userId) { + const found = await adminClient.getUserByEmail(testUser.email); + userId = found.data?.id || found.data?.data?.id; + } + if (userId) { + await adminClient.deleteUser(userId); + } + } + } catch { + // Best-effort — global teardown will sweep any stragglers + } } catch (error) { console.error('Failed to set up authenticated user:', error); throw error; diff --git a/e2e/tests/event-management.spec.ts b/e2e/tests/event-management.spec.ts index 39d22a0..294fb76 100644 --- a/e2e/tests/event-management.spec.ts +++ b/e2e/tests/event-management.spec.ts @@ -62,6 +62,25 @@ test.describe("Event Management", () => { createdEventIds = []; }); + test.afterAll(async () => { + // Delete the test creator user via admin account + if (!userId) return; + try { + const adminClient = new ApiClient(); + const loginRes = await adminClient.login( + "admin@nsc.dev", + "admin@admin123", + ); + const adminToken = loginRes.data?.token || loginRes.data?.data?.token; + if (adminToken) { + adminClient.setToken(adminToken); + await adminClient.deleteUser(userId); + } + } catch { + // Best-effort — global teardown will handle any remaining test users + } + }); + test("should create a new event via UI and redirect to event detail", async ({ page, browserName }) => { // Skip on Mobile Chrome due to MUI DatePicker mobile dialog interaction complexity test.skip(browserName === "chromium" && test.info().project.name === "Mobile Chrome", diff --git a/e2e/tests/event-registration.spec.ts b/e2e/tests/event-registration.spec.ts index 7ca8e41..094cc26 100644 --- a/e2e/tests/event-registration.spec.ts +++ b/e2e/tests/event-registration.spec.ts @@ -4,6 +4,7 @@ import { generateTestUser } from '../data/test-data'; test.describe('Event Registration', () => { let userToken: string; let eventId: string; + let userId: string; test.beforeAll(async ({ browser }) => { // Setup: Create user and event for registration tests @@ -18,6 +19,7 @@ test.describe('Event Registration', () => { if (signupResponse.ok()) { const data = await signupResponse.json(); userToken = data.token; + userId = data.user?.id || data.data?.user?.id; // Create an event const createEventResponse = await page.request.post('http://localhost:3000/api/event-registration', { @@ -43,6 +45,46 @@ test.describe('Event Registration', () => { await page.close(); }); + test.afterAll(async ({ browser }) => { + const page = await browser.newPage(); + try { + // Delete the test event + if (eventId && userToken) { + await page.request.delete( + `http://localhost:3000/api/events/remove/${eventId}`, + { + headers: { Authorization: `Bearer ${userToken}` }, + }, + ); + } + // Delete the test user via admin + if (userId) { + const adminRes = await page.request.post( + "http://localhost:3000/api/auth/login", + { + data: { email: "admin@nsc.dev", password: "admin@admin123" }, + }, + ); + if (adminRes.ok()) { + const adminData = await adminRes.json(); + const adminToken = adminData.token || adminData.data?.token; + if (adminToken) { + await page.request.delete( + `http://localhost:3000/api/users/remove/${userId}`, + { + headers: { Authorization: `Bearer ${adminToken}` }, + }, + ); + } + } + } + } catch { + // Best-effort — global teardown will handle stragglers + } finally { + await page.close(); + } + }); + test('should register for an event', async ({ page }) => { // Authenticate via localStorage await page.goto('/'); diff --git a/e2e/tests/user-management.spec.ts b/e2e/tests/user-management.spec.ts index a371d22..d0ba014 100644 --- a/e2e/tests/user-management.spec.ts +++ b/e2e/tests/user-management.spec.ts @@ -26,6 +26,7 @@ async function setAuthToken(page: Page, token: string): Promise { test.describe('User Management & Admin Functions', () => { let adminToken: string; let testUserId: string; + let testAdminUserId: string; test.beforeAll(async ({ browser }) => { // Setup: Create admin user @@ -43,6 +44,7 @@ test.describe('User Management & Admin Functions', () => { if (signupResponse.ok()) { const data = await signupResponse.json(); adminToken = data.token; + testAdminUserId = data.user?.id || data.data?.user?.id; // Create a regular user for role management tests const regularUser = generateTestUser(); @@ -59,6 +61,43 @@ test.describe('User Management & Admin Functions', () => { await page.close(); }); + test.afterAll(async ({ browser }) => { + const page = await browser.newPage(); + try { + const adminRes = await page.request.post( + "http://localhost:3000/api/auth/login", + { + data: { email: "admin@nsc.dev", password: "admin@admin123" }, + }, + ); + if (adminRes.ok()) { + const adminData = await adminRes.json(); + const seedAdminToken = adminData.token || adminData.data?.token; + if (seedAdminToken) { + const headers = { Authorization: `Bearer ${seedAdminToken}` }; + // Delete regular test user + if (testUserId) { + await page.request.delete( + `http://localhost:3000/api/users/remove/${testUserId}`, + { headers }, + ); + } + // Delete the test admin user created by this suite + if (testAdminUserId) { + await page.request.delete( + `http://localhost:3000/api/users/remove/${testAdminUserId}`, + { headers }, + ); + } + } + } + } catch { + // Best-effort — global teardown will handle stragglers + } finally { + await page.close(); + } + }); + test('should view user profile', async ({ page, browserName }) => { // Skip on Firefox due to CORS issues with API calls in authenticated context test.skip(browserName === "firefox", diff --git a/e2e/utils/api-client.ts b/e2e/utils/api-client.ts index 16b5b59..f5fb2be 100644 --- a/e2e/utils/api-client.ts +++ b/e2e/utils/api-client.ts @@ -4,7 +4,10 @@ export class ApiClient { private client: AxiosInstance; private token: string | null = null; - constructor(baseURL: string = process.env.PLAYWRIGHT_API_URL || "http://localhost:3000/api") { + constructor( + baseURL: string = process.env.PLAYWRIGHT_API_URL || + "http://localhost:3000/api", + ) { this.client = axios.create({ baseURL, headers: { @@ -28,7 +31,7 @@ export class ApiClient { firstName: string, lastName: string, pronouns: string = "they/them", - role: string = "user" + role: string = "user", ) { return this.client.post("/auth/signup", { email, @@ -91,6 +94,24 @@ export class ApiClient { this.token = null; delete this.client.defaults.headers.common["Authorization"]; } + + // ─── Admin helpers ─────────────────────────────────────────────────────────── + + async getAllUsers() { + return this.client.get("/users"); + } + + async getUserByEmail(email: string) { + return this.client.get(`/users/email/${encodeURIComponent(email)}`); + } + + async deleteUser(id: string) { + return this.client.delete(`/users/remove/${id}`); + } + + async getAllEvents(limit = 500) { + return this.client.get(`/events?numberOfEventsToGet=${limit}`); + } } export const createApiClient = () => new ApiClient(); diff --git a/e2e/utils/global-teardown.ts b/e2e/utils/global-teardown.ts index c7b0dd8..bf9fe6a 100644 --- a/e2e/utils/global-teardown.ts +++ b/e2e/utils/global-teardown.ts @@ -1,7 +1,119 @@ +import axios from "axios"; + +/** + * Global teardown — removes all data created by Playwright during the test run. + * + * Strategy: + * 1. Log in as the seed admin account. + * 2. Fetch all users whose email ends in "@example.com" (the domain used by + * every test-data generator in this suite). + * 3. Delete their events first (activities use SET NULL on creator delete, + * so we must remove events before users to avoid orphaned rows). + * 4. Delete the test users. + * + * Seed users (@nsc.dev) are never touched. + */ + +const API = process.env.PLAYWRIGHT_API_URL || "http://localhost:3000/api"; +const ADMIN_CREDENTIALS = { + email: "admin@nsc.dev", + password: "admin@admin123", +}; +const TEST_EMAIL_DOMAIN = "@example.com"; + async function globalTeardown() { - console.log('Running global teardown...'); - // Add any cleanup logic here if needed in the future - console.log('Global teardown completed'); + console.log( + "\nRunning global teardown — cleaning up Playwright test data...", + ); + + // ── 1. Obtain admin token ────────────────────────────────────────────────── + let adminToken: string; + try { + const res = await axios.post(`${API}/auth/login`, ADMIN_CREDENTIALS); + adminToken = res.data?.token || res.data?.data?.token; + if (!adminToken) throw new Error("No token in login response"); + } catch (err: any) { + console.error( + " ✗ Could not obtain admin token for teardown:", + err.message, + ); + console.log(" Skipping teardown — seed admin account may not exist yet."); + return; + } + + const client = axios.create({ + baseURL: API, + headers: { Authorization: `Bearer ${adminToken}` }, + }); + + // ── 2. Fetch all users and filter to test accounts ───────────────────────── + let testUsers: any[] = []; + try { + const res = await client.get("/users"); + const allUsers: any[] = res.data?.data || res.data || []; + testUsers = allUsers.filter( + (u: any) => + typeof u.email === "string" && u.email.endsWith(TEST_EMAIL_DOMAIN), + ); + } catch (err: any) { + console.error(" ✗ Could not fetch users:", err.message); + return; + } + + if (testUsers.length === 0) { + console.log(" ~ No test users found — nothing to clean up."); + console.log("Global teardown completed\n"); + return; + } + + const testUserIds = new Set(testUsers.map((u: any) => u.id)); + console.log(` Found ${testUsers.length} test user(s) to remove`); + + // ── 3. Delete events created by test users ───────────────────────────────── + let deletedEvents = 0; + try { + const eventsRes = await client.get(`/events?numberOfEventsToGet=500`); + // API may wrap in data.events, data.data, or return array directly + const raw = eventsRes.data; + const events: any[] = + raw?.data?.events ?? raw?.events ?? (Array.isArray(raw) ? raw : []); + + for (const event of events) { + const creatorId: string | undefined = + event.createdByUserId ?? event.creatorId ?? event.createdByUser?.id; + + if (creatorId && testUserIds.has(creatorId)) { + try { + await client.delete(`/events/remove/${event.id}`); + deletedEvents++; + } catch { + // Already deleted or not found — ignore + } + } + } + } catch (err: any) { + console.error(" ✗ Could not fetch/delete events:", err.message); + // Continue to user deletion even if event cleanup partial + } + + // ── 4. Delete test users ─────────────────────────────────────────────────── + let deletedUsers = 0; + for (const user of testUsers) { + try { + await client.delete(`/users/remove/${user.id}`); + deletedUsers++; + } catch (err: any) { + console.error( + ` ✗ Could not delete user "${user.email}":`, + err.response?.data?.message ?? err.message, + ); + } + } + + console.log( + ` ✓ Removed ${deletedEvents} event(s) and ${deletedUsers}/${testUsers.length} user(s)`, + ); + console.log("Global teardown completed\n"); } export default globalTeardown; diff --git a/playwright.config.ts b/playwright.config.ts index 9fcda93..2958722 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -4,66 +4,66 @@ const baseURL = process.env.PLAYWRIGHT_BASE_URL || 'http://localhost:8080'; const apiURL = process.env.PLAYWRIGHT_API_URL || 'http://localhost:3000'; export default defineConfig({ - testDir: './e2e/tests', + testDir: "./e2e/tests", fullyParallel: true, forbidOnly: !!process.env.CI, retries: process.env.CI ? 2 : 1, workers: process.env.CI ? 1 : undefined, reporter: [ - ['html', { outputFolder: 'playwright-report' }], - ['json', { outputFile: 'test-results/results.json' }], - ['junit', { outputFile: 'test-results/junit.xml' }], - ['list'], + ["html", { outputFolder: "playwright-report" }], + ["json", { outputFile: "test-results/results.json" }], + ["junit", { outputFile: "test-results/junit.xml" }], + ["list"], ], use: { baseURL, - trace: 'on-first-retry', - screenshot: 'only-on-failure', - video: 'retain-on-failure', + trace: "on-first-retry", + screenshot: "only-on-failure", + video: "retain-on-failure", }, projects: [ { - name: 'chromium', - use: { ...devices['Desktop Chrome'] }, + name: "chromium", + use: { ...devices["Desktop Chrome"] }, }, { - name: 'firefox', - use: { ...devices['Desktop Firefox'] }, + name: "firefox", + use: { ...devices["Desktop Firefox"] }, }, { - name: 'webkit', - use: { ...devices['Desktop Safari'] }, + name: "webkit", + use: { ...devices["Desktop Safari"] }, }, { - name: 'Mobile Chrome', - use: { ...devices['Pixel 5'] }, + name: "Mobile Chrome", + use: { ...devices["Pixel 5"] }, }, ], webServer: [ { command: process.env.CI - ? 'npm run start:prod --workspace=nsc-events-nestjs' - : 'npm run start:dev --workspace=nsc-events-nestjs', + ? "npm run start:prod --workspace=nsc-events-nestjs" + : "npm run start:dev --workspace=nsc-events-nestjs", port: 3000, reuseExistingServer: !process.env.CI, timeout: 120000, env: { - PATH: process.env.PATH || '', - NODE_ENV: process.env.NODE_ENV || 'test', - JWT_SECRET: process.env.JWT_SECRET || 'e2e-test-jwt-secret-key', - POSTGRES_HOST: process.env.POSTGRES_HOST || 'localhost', - POSTGRES_PORT: process.env.POSTGRES_PORT || '5432', - POSTGRES_USER: process.env.POSTGRES_USER || 'postgres', - POSTGRES_PASSWORD: process.env.POSTGRES_PASSWORD || 'postgres', - POSTGRES_DATABASE: process.env.POSTGRES_DATABASE || 'nsc_events', + PATH: process.env.PATH || "", + NODE_ENV: process.env.NODE_ENV || "test", + JWT_SECRET: process.env.JWT_SECRET || "e2e-test-jwt-secret-key", + POSTGRES_HOST: process.env.POSTGRES_HOST || "localhost", + POSTGRES_PORT: process.env.POSTGRES_PORT || "5432", + POSTGRES_USER: process.env.POSTGRES_USER || "postgres", + POSTGRES_PASSWORD: process.env.POSTGRES_PASSWORD || "postgres", + POSTGRES_DATABASE: process.env.POSTGRES_DATABASE || "nsc_events", }, }, { command: process.env.CI - ? 'npm run start --workspace=nsc-events-nextjs' - : 'npm run dev --workspace=nsc-events-nextjs', + ? "npm run start --workspace=nsc-events-nextjs" + : "npm run dev --workspace=nsc-events-nextjs", port: 8080, reuseExistingServer: !process.env.CI, timeout: 120000, @@ -72,5 +72,5 @@ export default defineConfig({ // Global setup/teardown commented out because webServer already handles service readiness // globalSetup: require.resolve('./e2e/utils/global-setup.ts'), - // globalTeardown: require.resolve('./e2e/utils/global-teardown.ts'), + globalTeardown: require.resolve("./e2e/utils/global-teardown.ts"), }); From daf084f8f0b9ce792d5234c917bb0ff1a2c3532e Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Apr 2026 10:22:43 -0700 Subject: [PATCH 6/6] Add database seed scripts and document seeding workflow (#194) * Initial plan * Add seed:all script, document seed commands in README, fix distinct admin passwords Agent-Logs-Url: https://github.com/SeattleColleges/nsc-events-fullstack/sessions/efec837b-8511-4043-b5a6-f3b5ee88f4c9 Co-authored-by: TVW96 <103076891+TVW96@users.noreply.github.com> * Give regular seed users distinct names and unique passwords Agent-Logs-Url: https://github.com/SeattleColleges/nsc-events-fullstack/sessions/b3a5e114-6dc5-4a01-b277-7490e7f7889f Co-authored-by: TVW96 <103076891+TVW96@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: TVW96 <103076891+TVW96@users.noreply.github.com> --- README.md | 30 ++++++++++++++++++++++++++++++ package.json | 3 ++- scripts/users-seed.js | 20 ++++++++++---------- 3 files changed, 42 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 1e279f6..f43e8e3 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,36 @@ npm install 2. Update the `.env` file in `nsc-events-nestjs` with your PostgreSQL credentials 3. The database tables will be automatically created when you start the backend +### Database Seeding + +Once both the backend and the database are running, you can populate the database with sample data for development: + +```bash +# Seed both users and events (recommended — runs users first, then events) +npm run seed:all + +# Seed only sample users (admin, creator, and regular user accounts) +npm run seed:users + +# Seed only sample events (requires the seed creator account to exist) +npm run seed +``` + +The seed scripts connect to the NestJS API (default: `http://localhost:3000/api`). Override the target with the `PLAYWRIGHT_API_URL` environment variable if your backend is on a different port. + +**Seeded accounts** + +| Email | Password | Role | +|-------|----------|------| +| `admin@nsc.dev` | `admin@admin123` | admin | +| `admin2@nsc.dev` | `Admin2@dev456` | admin | +| `seed-creator@nsc.dev` | `Seed@Creator123` | creator | +| `user@nsc.dev` | `User@Alex123` | user | +| `user2@nsc.dev` | `User@Sam456` | user | +| `user3@nsc.dev` | `User@Jordan789` | user | + +> **Note:** These accounts are for local development only. Do not use these credentials in production environments. + ### Running the Applications **Backend:** diff --git a/package.json b/package.json index 6bf069d..2f31e7b 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,8 @@ "docker": "docker compose down --rmi all --volumes --remove-orphans && docker compose build --no-cache && docker compose up -d", "docker:watch": "npm run docker && docker compose logs -f nestjs", "seed": "node scripts/events-seed.js", - "seed:users": "node scripts/users-seed.js" + "seed:users": "node scripts/users-seed.js", + "seed:all": "npm run seed:users && npm run seed" }, "devDependencies": { "@playwright/test": "^1.57.0", diff --git a/scripts/users-seed.js b/scripts/users-seed.js index 431ff0c..033fa92 100644 --- a/scripts/users-seed.js +++ b/scripts/users-seed.js @@ -39,7 +39,7 @@ const SEED_USERS = [ }, { email: "admin2@nsc.dev", - password: "admin@admin123", + password: "Admin2@dev456", firstName: "Admin2", lastName: "TestAccount", pronouns: "they/them", @@ -47,25 +47,25 @@ const SEED_USERS = [ }, { email: "user@nsc.dev", - password: "user@user123", - firstName: "User", - lastName: "TestAccount", + password: "User@Alex123", + firstName: "Alex", + lastName: "Rivera", pronouns: "they/them", role: "user", }, { email: "user2@nsc.dev", - password: "user@user123", - firstName: "User", - lastName: "TestAccount", + password: "User@Sam456", + firstName: "Sam", + lastName: "Park", pronouns: "she/her", role: "user", }, { email: "user3@nsc.dev", - password: "user@user123", - firstName: "User3", - lastName: "TestAccount", + password: "User@Jordan789", + firstName: "Jordan", + lastName: "Lee", pronouns: "they/them", role: "user", },