diff --git a/e2e/api.test.ts b/e2e/api.jest.ts similarity index 100% rename from e2e/api.test.ts rename to e2e/api.jest.ts diff --git a/e2e/api_date_sort.test.ts b/e2e/api_date_sort.jest.ts similarity index 100% rename from e2e/api_date_sort.test.ts rename to e2e/api_date_sort.jest.ts diff --git a/e2e/api_location_filter.test.ts b/e2e/api_location_filter.jest.ts similarity index 100% rename from e2e/api_location_filter.test.ts rename to e2e/api_location_filter.jest.ts diff --git a/e2e/api_no_postings.test.ts b/e2e/api_no_postings.jest.ts similarity index 100% rename from e2e/api_no_postings.test.ts rename to e2e/api_no_postings.jest.ts diff --git a/e2e/api_part_time.test.ts b/e2e/api_part_time.jest.ts similarity index 100% rename from e2e/api_part_time.test.ts rename to e2e/api_part_time.jest.ts diff --git a/e2e/api_sent_emails.test.ts b/e2e/api_sent_emails.jest.ts similarity index 100% rename from e2e/api_sent_emails.test.ts rename to e2e/api_sent_emails.jest.ts diff --git a/e2e/job_posting.jest.ts b/e2e/job_posting.jest.ts new file mode 100644 index 0000000..12306fe --- /dev/null +++ b/e2e/job_posting.jest.ts @@ -0,0 +1,82 @@ +import puppeteer, { Browser, Page } from 'puppeteer'; +import { config } from './config'; + +const BASE_URL = config.BASE_URL; +const email = 'cboilley@my.bcit.ca'; +const password = 'Bcit@123456'; + +let browser: Browser; +let page: Page; + +beforeAll(async () => { + browser = await puppeteer.launch(); + page = await browser.newPage(); +}); + +afterAll(async () => { + await browser.close(); +}); + +const login = async () => { + await page.goto(`${BASE_URL}/`); + await page.click('a[href="/api/auth/login"]'); + await page.waitForSelector('input[name="username"]'); + await page.type('input[name="username"]', email); + + await page.type('input[id="password"]', password); + await page.click('button[name="action"]'); +}; + +test('Create Job Posting', async () => { + let browserType; + + try { + browserType = await browser.userAgent(); + + // Login + await login(); + + // Your test implementation here + // Example: + // Wait for the button to appear in the DOM + await page.waitForSelector( + 'button[class="inline-flex items-center justify-center rounded-md ring-offset-white transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-gray-950 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 bg-gray-100 text-gray-900 hover:bg-gray-100/80 h-10 px-4 py-2 whitespace-nowrap text-base leading-6 font-medium"]' + ); + + // Click the button + await page.click( + 'button[class="inline-flex items-center justify-center rounded-md ring-offset-white transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-gray-950 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 bg-gray-100 text-gray-900 hover:bg-gray-100/80 h-10 px-4 py-2 whitespace-nowrap text-base leading-6 font-medium"]' + ); + + await page.setDefaultTimeout(2000); + + // Fill in job details + await page.click('input[placeholder="Job Title"]'); + await page.type('input[placeholder="Job Title"]', `${browserType} Test`); + await page.type('input[name="hiringOrganization"]', 'Tester Inc'); + await page.type('input[name="streetAddress"]', '123 Testing'); + await page.type('input[name="addressLocality"]', 'VAN'); + await page.select('select[name="addressRegion"]', 'BC'); + await page.type('input[name="language"]', 'EN'); + await page.type('input[name="employmentType"]', 'Tester'); + await page.type('input[name="jobType"]', 'Internship'); + await page.type('input[name="minWage"]', '5'); + await page.type('input[name="maxWage"]', '9'); + await page.type('textarea[name="benefits"]', 'Health'); + await page.type('input[name="startDate"]', '2024-05-21'); + await page.type('input[name="validThrough"]', '2024-05-31'); + await page.type('textarea[name="description"]', 'I am a tester'); + await page.click('label[for="displayOnNewcomers"]'); + await page.click('input[name="displayOnVulnerableYouth"]'); + await page.click('button[name="Add Posting"]'); + + // Assertions and further actions + expect(page.url()).toBe(`${BASE_URL}/admin-panel/home`); + console.log('Create Job Posting and check info passed'); + } catch (error) { + console.log('Create Job Posting and check info failed'); + console.error(error); + } +}); + +// Repeat similar structure for other tests: Edit Job Posting and Delete Job Posting diff --git a/e2e/login.test.ts b/e2e/login.jest.ts similarity index 100% rename from e2e/login.test.ts rename to e2e/login.jest.ts diff --git a/e2e/logout.test.ts b/e2e/logout.jest.ts similarity index 100% rename from e2e/logout.test.ts rename to e2e/logout.jest.ts diff --git a/e2e/signup.test.ts b/e2e/signup.jest.ts similarity index 100% rename from e2e/signup.test.ts rename to e2e/signup.jest.ts diff --git a/jest.config.ts b/jest.config.ts index 1ed492f..1338b74 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -1,6 +1,6 @@ module.exports = { preset: 'jest-playwright-preset', - testMatch: ['**/e2e/*.test.ts'], + testMatch: ['**/e2e/*.jest.ts'], transform: { '^.+\\.ts$': 'ts-jest', }, diff --git a/package-lock.json b/package-lock.json index d43f8d4..7918d6f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,7 +30,6 @@ "mongoose": "^8.3.4", "next": "14.2.3", "node-cron": "^3.0.3", - "puppeteer": "^22.9.0", "puppeteer-extra": "^3.3.6", "puppeteer-extra-plugin-stealth": "^2.11.2", "react": "^18", @@ -46,6 +45,7 @@ "@playwright/test": "^1.44.1", "@types/jest": "^29.5.12", "@types/node": "^20.12.12", + "@types/puppeteer": "^7.0.4", "@types/react": "18.3.3", "eslint": "^8", "eslint-config-next": "14.2.3", @@ -56,6 +56,7 @@ "playwright": "^1.44.1", "postcss": "^8", "prettier": "3.2.5", + "puppeteer": "^22.10.0", "tailwindcss": "^3.4.1", "ts-jest": "^29.1.3", "typescript": "^5.4.5" @@ -1590,6 +1591,7 @@ "version": "2.2.3", "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.2.3.tgz", "integrity": "sha512-bJ0UBsk0ESOs6RFcLXOt99a3yTDcOKlzfjad+rhFwdaG1Lu/Wzq58GHYCDTlZ9z6mldf4g+NTb+TXEfe0PpnsQ==", + "devOptional": true, "dependencies": { "debug": "4.3.4", "extract-zip": "2.0.1", @@ -1611,6 +1613,7 @@ "version": "7.6.0", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "devOptional": true, "dependencies": { "lru-cache": "^6.0.0" }, @@ -2357,7 +2360,8 @@ "node_modules/@tootallnate/quickjs-emscripten": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", - "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==" + "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", + "devOptional": true }, "node_modules/@tsconfig/node10": { "version": "1.0.11", @@ -2496,6 +2500,16 @@ "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==", "devOptional": true }, + "node_modules/@types/puppeteer": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/@types/puppeteer/-/puppeteer-7.0.4.tgz", + "integrity": "sha512-ja78vquZc8y+GM2al07GZqWDKQskQXygCDiu0e3uO0DMRKqE0MjrFBFmTulfPYzLB6WnL7Kl2tFPy0WXSpPomg==", + "deprecated": "This is a stub types definition. puppeteer provides its own type definitions, so you do not need this installed.", + "devOptional": true, + "dependencies": { + "puppeteer": "*" + } + }, "node_modules/@types/quill": { "version": "1.3.10", "resolved": "https://registry.npmjs.org/@types/quill/-/quill-1.3.10.tgz", @@ -2561,6 +2575,7 @@ "version": "2.10.3", "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", + "dev": true, "optional": true, "dependencies": { "@types/node": "*" @@ -2731,6 +2746,7 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "devOptional": true, "dependencies": { "debug": "^4.3.4" }, @@ -2844,7 +2860,8 @@ "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "devOptional": true }, "node_modules/aria-hidden": { "version": "1.2.4", @@ -3046,6 +3063,7 @@ "version": "0.13.4", "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", + "devOptional": true, "dependencies": { "tslib": "^2.0.1" }, @@ -3122,7 +3140,8 @@ "node_modules/b4a": { "version": "1.6.6", "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.6.tgz", - "integrity": "sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==" + "integrity": "sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==", + "devOptional": true }, "node_modules/babel": { "version": "6.23.0", @@ -3257,12 +3276,14 @@ "version": "2.2.2", "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.2.2.tgz", "integrity": "sha512-h7z00dWdG0PYOQEvChhOSWvOfkIKsdZGkWr083FgN/HyoQuebSew/cgirYqh9SCuy/hRvxc5Vy6Fw8xAmYHLkQ==", + "dev": true, "optional": true }, "node_modules/bare-fs": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.3.0.tgz", "integrity": "sha512-TNFqa1B4N99pds2a5NYHR15o0ZpdNKbAeKTE/+G6ED/UeOavv8RY3dr/Fu99HW3zU3pXpo2kDNO8Sjsm2esfOw==", + "dev": true, "optional": true, "dependencies": { "bare-events": "^2.0.0", @@ -3274,12 +3295,14 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.3.0.tgz", "integrity": "sha512-oPb8oMM1xZbhRQBngTgpcQ5gXw6kjOaRsSWsIeNyRxGed2w/ARyP7ScBYpWR1qfX2E5rS3gBw6OWcSQo+s+kUg==", + "dev": true, "optional": true }, "node_modules/bare-path": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-2.1.3.tgz", "integrity": "sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA==", + "dev": true, "optional": true, "dependencies": { "bare-os": "^2.1.0" @@ -3289,6 +3312,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-1.0.0.tgz", "integrity": "sha512-KhNUoDL40iP4gFaLSsoGE479t0jHijfYdIcxRn/XtezA2BaUD0NRf/JGRpsMq6dMNM+SrCrB0YSSo/5wBY4rOQ==", + "dev": true, "optional": true, "dependencies": { "streamx": "^2.16.1" @@ -3298,6 +3322,7 @@ "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "devOptional": true, "funding": [ { "type": "github", @@ -3317,6 +3342,7 @@ "version": "5.0.5", "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", + "devOptional": true, "engines": { "node": ">=10.0.0" } @@ -3416,6 +3442,7 @@ "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "devOptional": true, "funding": [ { "type": "github", @@ -3439,6 +3466,7 @@ "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "devOptional": true, "engines": { "node": "*" } @@ -3539,6 +3567,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "devOptional": true, "engines": { "node": ">=6" } @@ -3641,6 +3670,7 @@ "version": "0.5.19", "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.5.19.tgz", "integrity": "sha512-UA6zL77b7RYCjJkZBsZ0wlvCTD+jTjllZ8f6wdO4buevXgTZYjV+XLB9CiEa2OuuTGGTLnI7eN9I60YxuALGQg==", + "devOptional": true, "dependencies": { "mitt": "3.0.1", "urlpattern-polyfill": "10.0.0", @@ -3789,6 +3819,7 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "devOptional": true, "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", @@ -3801,12 +3832,14 @@ "node_modules/cliui/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "devOptional": true }, "node_modules/cliui/node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "devOptional": true, "engines": { "node": ">=8" } @@ -3815,6 +3848,7 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "devOptional": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -3828,6 +3862,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "devOptional": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -3957,6 +3992,7 @@ "version": "9.0.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", + "devOptional": true, "dependencies": { "env-paths": "^2.2.1", "import-fresh": "^3.3.0", @@ -4057,6 +4093,7 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", + "devOptional": true, "engines": { "node": ">= 14" } @@ -4244,6 +4281,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", + "devOptional": true, "dependencies": { "ast-types": "^0.13.4", "escodegen": "^2.1.0", @@ -4287,7 +4325,8 @@ "node_modules/devtools-protocol": { "version": "0.0.1286932", "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1286932.tgz", - "integrity": "sha512-wu58HMQll9voDjR4NlPyoDEw1syfzaBNHymMMZ/QOXiHRNluOnDgu9hp1yHOKYoMlxCh4lSSiugLITe6Fvu1eA==" + "integrity": "sha512-wu58HMQll9voDjR4NlPyoDEw1syfzaBNHymMMZ/QOXiHRNluOnDgu9hp1yHOKYoMlxCh4lSSiugLITe6Fvu1eA==", + "devOptional": true }, "node_modules/didyoumean": { "version": "1.2.2", @@ -4382,6 +4421,7 @@ "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "devOptional": true, "dependencies": { "once": "^1.4.0" } @@ -4403,6 +4443,7 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "devOptional": true, "engines": { "node": ">=6" } @@ -4411,6 +4452,7 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "devOptional": true, "dependencies": { "is-arrayish": "^0.2.1" } @@ -4601,6 +4643,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "devOptional": true, "dependencies": { "esprima": "^4.0.1", "estraverse": "^5.2.0", @@ -5027,6 +5070,7 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "devOptional": true, "engines": { "node": ">=4.0" } @@ -5035,6 +5079,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "devOptional": true, "engines": { "node": ">=0.10.0" } @@ -5120,6 +5165,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "devOptional": true, "dependencies": { "debug": "^4.1.1", "get-stream": "^5.1.0", @@ -5139,6 +5185,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "devOptional": true, "dependencies": { "pump": "^3.0.0" }, @@ -5163,7 +5210,8 @@ "node_modules/fast-fifo": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", - "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==" + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "devOptional": true }, "node_modules/fast-glob": { "version": "3.3.2", @@ -5224,6 +5272,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "devOptional": true, "dependencies": { "pend": "~1.2.0" } @@ -5484,6 +5533,7 @@ "version": "11.2.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "devOptional": true, "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -5557,6 +5607,7 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "devOptional": true, "engines": { "node": "6.* || 8.* || >= 10.*" } @@ -5652,6 +5703,7 @@ "version": "6.0.3", "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.3.tgz", "integrity": "sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==", + "devOptional": true, "dependencies": { "basic-ftp": "^5.0.2", "data-uri-to-buffer": "^6.0.2", @@ -5964,6 +6016,7 @@ "version": "7.0.2", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "devOptional": true, "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" @@ -5976,6 +6029,7 @@ "version": "7.0.4", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz", "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==", + "devOptional": true, "dependencies": { "agent-base": "^7.0.2", "debug": "4" @@ -6012,6 +6066,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "devOptional": true, "funding": [ { "type": "github", @@ -6040,6 +6095,7 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "devOptional": true, "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -6135,6 +6191,7 @@ "version": "9.0.5", "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "devOptional": true, "dependencies": { "jsbn": "1.1.0", "sprintf-js": "^1.1.3" @@ -6177,7 +6234,8 @@ "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "devOptional": true }, "node_modules/is-async-function": { "version": "2.0.0", @@ -7568,6 +7626,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "devOptional": true, "dependencies": { "argparse": "^2.0.1" }, @@ -7578,7 +7637,8 @@ "node_modules/jsbn": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", - "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==" + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", + "devOptional": true }, "node_modules/jsesc": { "version": "2.5.2", @@ -7600,7 +7660,8 @@ "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "devOptional": true }, "node_modules/json-schema-traverse": { "version": "0.4.1", @@ -8228,7 +8289,8 @@ "node_modules/mitt": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", - "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==" + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "devOptional": true }, "node_modules/mixin-object": { "version": "2.0.1", @@ -8391,6 +8453,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", + "devOptional": true, "engines": { "node": ">= 0.4.0" } @@ -9108,6 +9171,7 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.0.1.tgz", "integrity": "sha512-ASV8yU4LLKBAjqIPMbrgtaKIvxQri/yh2OpI+S6hVa9JRkUI3Y3NPFbfngDtY7oFtSMD3w31Xns89mDa3Feo5A==", + "devOptional": true, "dependencies": { "@tootallnate/quickjs-emscripten": "^0.23.0", "agent-base": "^7.0.2", @@ -9126,6 +9190,7 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", + "devOptional": true, "dependencies": { "degenerator": "^5.0.0", "netmask": "^2.0.2" @@ -9158,6 +9223,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "devOptional": true, "dependencies": { "callsites": "^3.0.0" }, @@ -9169,6 +9235,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "devOptional": true, "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", @@ -9255,7 +9322,8 @@ "node_modules/pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==" + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "devOptional": true }, "node_modules/picocolors": { "version": "1.0.1", @@ -9606,6 +9674,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "devOptional": true, "engines": { "node": ">=0.4.0" } @@ -9638,6 +9707,7 @@ "version": "6.4.0", "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.4.0.tgz", "integrity": "sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==", + "devOptional": true, "dependencies": { "agent-base": "^7.0.2", "debug": "^4.3.4", @@ -9656,6 +9726,7 @@ "version": "7.18.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "devOptional": true, "engines": { "node": ">=12" } @@ -9669,6 +9740,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "devOptional": true, "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" @@ -9686,6 +9758,7 @@ "version": "22.10.0", "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-22.10.0.tgz", "integrity": "sha512-ZOkZd6a6t0BdKcWb0wAYHWQqCfdlN1PPnXOmg/XNrbo6gJhYWFX4qCNb6ahSn8TpAqBqLCoD4Q010F7GwOM7mA==", + "devOptional": true, "hasInstallScript": true, "dependencies": { "@puppeteer/browsers": "2.2.3", @@ -9704,6 +9777,7 @@ "version": "22.10.0", "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-22.10.0.tgz", "integrity": "sha512-I54J4Vy4I07UHsgB1QSmuFoF7KNQjJWcvFBPhtY+ezMdBfwgGDr8dzYrJa11aPgP9kxIUHjhktcMmmfJkOAtTw==", + "devOptional": true, "dependencies": { "@puppeteer/browsers": "2.2.3", "chromium-bidi": "0.5.19", @@ -9911,7 +9985,8 @@ "node_modules/queue-tick": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", - "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==" + "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==", + "devOptional": true }, "node_modules/quill": { "version": "1.3.7", @@ -10132,6 +10207,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "devOptional": true, "engines": { "node": ">=0.10.0" } @@ -10196,6 +10272,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "devOptional": true, "engines": { "node": ">=4" } @@ -10568,6 +10645,7 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "devOptional": true, "engines": { "node": ">= 6.0.0", "npm": ">= 3.0.0" @@ -10577,6 +10655,7 @@ "version": "2.8.3", "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", + "devOptional": true, "dependencies": { "ip-address": "^9.0.5", "smart-buffer": "^4.2.0" @@ -10590,6 +10669,7 @@ "version": "8.0.3", "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.3.tgz", "integrity": "sha512-VNegTZKhuGq5vSD6XNKlbqWhyt/40CgoEw8XxD6dhnm8Jq9IEa3nIa4HwnM8XOqU0CdB0BwWVXusqiFXfHB3+A==", + "devOptional": true, "dependencies": { "agent-base": "^7.1.1", "debug": "^4.3.4", @@ -10603,7 +10683,7 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "devOptional": true, + "dev": true, "engines": { "node": ">=0.10.0" } @@ -10724,7 +10804,8 @@ "node_modules/sprintf-js": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", - "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==" + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "devOptional": true }, "node_modules/stack-utils": { "version": "2.0.6", @@ -10759,6 +10840,7 @@ "version": "2.16.1", "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.16.1.tgz", "integrity": "sha512-m9QYj6WygWyWa3H1YY69amr4nVgy61xfjys7xO7kviL5rfIEc2naf+ewFiOA+aEJD7y0JO3h2GoiUv4TDwEGzQ==", + "devOptional": true, "dependencies": { "fast-fifo": "^1.1.0", "queue-tick": "^1.0.1" @@ -11159,6 +11241,7 @@ "version": "3.0.5", "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.5.tgz", "integrity": "sha512-JOgGAmZyMgbqpLwct7ZV8VzkEB6pxXFBVErLtb+XCOqzc6w1xiWKI9GVd6bwk68EX7eJ4DWmfXVmq8K2ziZTGg==", + "devOptional": true, "dependencies": { "pump": "^3.0.0", "tar-stream": "^3.1.5" @@ -11172,6 +11255,7 @@ "version": "3.1.7", "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", + "devOptional": true, "dependencies": { "b4a": "^1.6.4", "fast-fifo": "^1.2.0", @@ -11239,7 +11323,8 @@ "node_modules/through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "devOptional": true }, "node_modules/tmpl": { "version": "1.0.5", @@ -11572,6 +11657,7 @@ "version": "1.4.3", "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", + "devOptional": true, "dependencies": { "buffer": "^5.2.1", "through": "^2.3.8" @@ -11636,7 +11722,8 @@ "node_modules/urlpattern-polyfill": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz", - "integrity": "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==" + "integrity": "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==", + "devOptional": true }, "node_modules/use-callback-ref": { "version": "1.3.2", @@ -12088,6 +12175,7 @@ "version": "8.17.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.0.tgz", "integrity": "sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==", + "devOptional": true, "engines": { "node": ">=10.0.0" }, @@ -12108,6 +12196,7 @@ "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "devOptional": true, "engines": { "node": ">=10" } @@ -12132,6 +12221,7 @@ "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "devOptional": true, "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", @@ -12149,6 +12239,7 @@ "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "devOptional": true, "engines": { "node": ">=12" } @@ -12156,12 +12247,14 @@ "node_modules/yargs/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "devOptional": true }, "node_modules/yargs/node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "devOptional": true, "engines": { "node": ">=8" } @@ -12170,6 +12263,7 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "devOptional": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -12183,6 +12277,7 @@ "version": "2.10.0", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "devOptional": true, "dependencies": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" @@ -12212,6 +12307,7 @@ "version": "3.22.4", "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz", "integrity": "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==", + "devOptional": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/package.json b/package.json index a74de6d..d1f5f32 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,8 @@ "lint": "next lint", "prepare": "husky", "format": "eslint --fix . && prettier --write .", - "jest": "jest e2e" + "jest": "jest e2e", + "coverage": "jest e2e --coverage" }, "lint-staged": { "**/*": [ @@ -40,7 +41,6 @@ "mongoose": "^8.3.4", "next": "14.2.3", "node-cron": "^3.0.3", - "puppeteer": "^22.9.0", "puppeteer-extra": "^3.3.6", "puppeteer-extra-plugin-stealth": "^2.11.2", "react": "^18", @@ -56,6 +56,7 @@ "@playwright/test": "^1.44.1", "@types/jest": "^29.5.12", "@types/node": "^20.12.12", + "@types/puppeteer": "^7.0.4", "@types/react": "18.3.3", "eslint": "^8", "eslint-config-next": "14.2.3", @@ -66,6 +67,7 @@ "playwright": "^1.44.1", "postcss": "^8", "prettier": "3.2.5", + "puppeteer": "^22.10.0", "tailwindcss": "^3.4.1", "ts-jest": "^29.1.3", "typescript": "^5.4.5" diff --git a/tests/api.test.ts b/tests/api.test.ts new file mode 100644 index 0000000..07eee32 --- /dev/null +++ b/tests/api.test.ts @@ -0,0 +1,77 @@ +import { test, expect, APIRequestContext } from '@playwright/test'; +import { config } from './config'; + +const baseURL = `${config.BASE_URL}/api/job-posting`; + +// Function to validate job properties +const validateJobProperties = (job: any) => { + expect(job).toHaveProperty('_id'); + // expect(job).toHaveProperty('jobTitle'); + // expect(job).toHaveProperty('datePosted'); + // expect(job).toHaveProperty('hiringOrganization'); + // expect(job).toHaveProperty('streetAddress'); + // expect(job).toHaveProperty('addressLocality'); + // expect(job).toHaveProperty('addressRegion'); + // expect(job).toHaveProperty('minCompValue'); + // expect(job).toHaveProperty('maxCompValue'); + // expect(job).toHaveProperty('compTimeUnit'); + // expect(job).toHaveProperty('workHours'); + // expect(job).toHaveProperty('specialCommitments'); + // expect(job).toHaveProperty('email'); + // expect(job).toHaveProperty('jobPageId'); + // expect(job).toHaveProperty('employmentType'); + // expect(job).toHaveProperty('employmentSubType'); + // expect(job).toHaveProperty('startTime'); + // expect(job).toHaveProperty('benefits'); + // expect(job).toHaveProperty('vacancies'); + // expect(job).toHaveProperty('verified'); + // expect(job).toHaveProperty('validThrough'); + // expect(job).toHaveProperty('description'); + // expect(job).toHaveProperty('site1'); + // expect(job).toHaveProperty('site2'); + // expect(job).toHaveProperty('site3'); + // expect(job).toHaveProperty('site4'); + // expect(job).toHaveProperty('__v'); + // expect(job).toHaveProperty('sent'); +}; + +// Function to test the endpoints +const testEndpoint = async (context: APIRequestContext, endpoint: string) => { + const response = await context.get(`${baseURL}/${endpoint}?page_num=1`); + expect(response.status()).toBe(200); + const data = await response.json(); + + for (const joblist in data) { + for (const job of data[joblist]) { + validateJobProperties(job); + } + expect(data[joblist].length).toBeGreaterThan(0); + } +}; + +// Test for each endpoint +test.describe('API Job Postings', () => { + let context: APIRequestContext; + + test.beforeAll(async ({ playwright }) => { + context = await playwright.request.newContext(); + }); + + test.afterAll(async () => { + await context.dispose(); + }); + + const endpoints = [ + // 'newcomers', + // 'disabled', + // 'indigenous', + // 'students', + 'asylum-refugees', + ]; + + endpoints.forEach(endpoint => { + test(`API: ${endpoint.charAt(0).toUpperCase() + endpoint.slice(1)} Postings`, async () => { + await testEndpoint(context, endpoint); + }); + }); +}); diff --git a/tests/api_date_sort.test.ts b/tests/api_date_sort.test.ts new file mode 100644 index 0000000..b6d0440 --- /dev/null +++ b/tests/api_date_sort.test.ts @@ -0,0 +1,103 @@ +import { test, expect, APIRequestContext } from '@playwright/test'; +import { config } from './config'; + +const baseURL = `${config.BASE_URL}/api/job-posting`; + +// Function to validate job properties +const validateJobProperties = (job: any) => { + expect(job).toHaveProperty('_id'); + // expect(job).toHaveProperty('jobTitle'); + // expect(job).toHaveProperty('datePosted'); + // expect(job).toHaveProperty('hiringOrganization'); + // expect(job).toHaveProperty('streetAddress'); + // expect(job).toHaveProperty('addressLocality'); + // expect(job).toHaveProperty('addressRegion'); + // expect(job).toHaveProperty('minCompValue'); + // expect(job).toHaveProperty('maxCompValue'); + // expect(job).toHaveProperty('compTimeUnit'); + // expect(job).toHaveProperty('workHours'); + // expect(job).toHaveProperty('specialCommitments'); + // expect(job).toHaveProperty('email'); + // expect(job).toHaveProperty('jobPageId'); + // expect(job).toHaveProperty('employmentType'); + // expect(job).toHaveProperty('employmentSubType'); + // expect(job).toHaveProperty('startTime'); + // expect(job).toHaveProperty('benefits'); + // expect(job).toHaveProperty('vacancies'); + // expect(job).toHaveProperty('verified'); + // expect(job).toHaveProperty('validThrough'); + // expect(job).toHaveProperty('description'); + // expect(job).toHaveProperty('site1'); + // expect(job).toHaveProperty('site2'); + // expect(job).toHaveProperty('site3'); + // expect(job).toHaveProperty('site4'); + // expect(job).toHaveProperty('__v'); + // expect(job).toHaveProperty('sent'); +}; + +// Function to test the endpoints +const testEndpoint = async ( + context: APIRequestContext, + endpoint: string, + sortOrder: string +) => { + const response = await context.get( + `${baseURL}/${endpoint}?sort=${sortOrder}&page_num=1` + ); + expect(response.status()).toBe(200); + + let data; + try { + data = await response.json(); + } catch (error) { + throw new Error( + `Error parsing JSON response for ${endpoint} with sort ${sortOrder}: ${error}` + ); + } + + if (typeof data !== 'object' || Array.isArray(data)) { + throw new Error( + `Unexpected response format for ${endpoint} with sort ${sortOrder}` + ); + } + for (const joblist in data) { + for (const job of data[joblist]) { + validateJobProperties(job); + } + expect(data[joblist].length).toBeGreaterThan(0); + } +}; + +// Test for each endpoint and sort order +test.describe('API Job Postings', () => { + let context: APIRequestContext; + + test.beforeAll(async ({ playwright }) => { + context = await playwright.request.newContext(); + }); + + test.afterAll(async () => { + await context.dispose(); + }); + + const endpoints = [ + 'newcomers', + 'disabled', + 'indigenous', + 'students', + 'asylum-refugees', + ]; + + const sortOrders = [ + 'a', // ascending + 'd', // descending + ]; + + endpoints.forEach(endpoint => { + sortOrders.forEach(sortOrder => { + test(`API: ${endpoint.charAt(0).toUpperCase() + endpoint.slice(1)} Postings sorted ${sortOrder === 'a' ? 'ascending' : 'descending'}`, async () => { + await testEndpoint(context, endpoint, sortOrder); + }); + }); + }); +}); diff --git a/tests/api_location_filter.test.ts b/tests/api_location_filter.test.ts new file mode 100644 index 0000000..58362f3 --- /dev/null +++ b/tests/api_location_filter.test.ts @@ -0,0 +1,97 @@ +import { test, expect, APIRequestContext } from '@playwright/test'; +import { config } from './config'; + +const baseURL = `${config.BASE_URL}/api/job-posting`; + +// Function to validate job properties +const validateJobProperties = (job: any) => { + expect(job).toHaveProperty('_id'); + // expect(job).toHaveProperty('jobTitle'); + // expect(job).toHaveProperty('datePosted'); + // expect(job).toHaveProperty('hiringOrganization'); + // expect(job).toHaveProperty('streetAddress'); + // expect(job).toHaveProperty('addressLocality'); + // expect(job).toHaveProperty('addressRegion'); + // expect(job).toHaveProperty('minCompValue'); + // expect(job).toHaveProperty('maxCompValue'); + // expect(job).toHaveProperty('compTimeUnit'); + // expect(job).toHaveProperty('workHours'); + // expect(job).toHaveProperty('specialCommitments'); + // expect(job).toHaveProperty('email'); + // expect(job).toHaveProperty('jobPageId'); + // expect(job).toHaveProperty('employmentType'); + // expect(job).toHaveProperty('employmentSubType'); + // expect(job).toHaveProperty('startTime'); + // expect(job).toHaveProperty('benefits'); + // expect(job).toHaveProperty('vacancies'); + // expect(job).toHaveProperty('verified'); + // expect(job).toHaveProperty('validThrough'); + // expect(job).toHaveProperty('description'); + // expect(job).toHaveProperty('site1'); + // expect(job).toHaveProperty('site2'); + // expect(job).toHaveProperty('site3'); + // expect(job).toHaveProperty('site4'); + // expect(job).toHaveProperty('__v'); + // expect(job).toHaveProperty('sent'); +}; + +// Function to test the endpoints +const testEndpoint = async ( + context: APIRequestContext, + endpoint: string, + province: string +) => { + const response = await context.get( + `${baseURL}/${endpoint}?p=${province}&page_num=1` + ); + expect(response.status()).toBe(200); + const data = await response.json(); + + for (const joblist in data) { + for (const job of data[joblist]) { + validateJobProperties(job); + } + expect(data[joblist].length).toBe(25); + } +}; + +// Test for each endpoint and province +test.describe('API Job Postings', () => { + let context: APIRequestContext; + + test.beforeAll(async ({ playwright }) => { + context = await playwright.request.newContext(); + }); + + test.afterAll(async () => { + await context.dispose(); + }); + + const endpoints = [ + 'newcomers', + 'disabled', + 'indigenous', + 'students', + // 'asylum-refugees', currently is not populated + ]; + + const provinces = [ + // 'AB', + // 'BC', + // 'MB', + // 'NB', + // 'NL', + // 'NT', + // 'NS', + // 'NU', + 'ON', + ]; + + endpoints.forEach(endpoint => { + provinces.forEach(province => { + test(`API: ${endpoint.charAt(0).toUpperCase() + endpoint.slice(1)} Postings for ${province}`, async () => { + await testEndpoint(context, endpoint, province); + }); + }); + }); +}); diff --git a/tests/api_no_postings.test.ts b/tests/api_no_postings.test.ts new file mode 100644 index 0000000..aa35f3a --- /dev/null +++ b/tests/api_no_postings.test.ts @@ -0,0 +1,37 @@ +import { test, expect, APIRequestContext } from '@playwright/test'; +import { config } from './config'; + +const baseURL = `${config.BASE_URL}/api/job-posting`; + +// Function to test the endpoints +const testEndpoint = async (context: APIRequestContext, endpoint: string) => { + const response = await context.get(`${baseURL}/${endpoint}?page_num=200`); + expect(response.status()).toBe(204); +}; + +// Test for each endpoint +test.describe('API Job Postings', () => { + let context: APIRequestContext; + + test.beforeAll(async ({ playwright }) => { + context = await playwright.request.newContext(); + }); + + test.afterAll(async () => { + await context.dispose(); + }); + + const endpoints = [ + 'newcomers', + 'disabled', + 'indigenous', + 'students', + 'asylum-refugees', + ]; + + endpoints.forEach(endpoint => { + test(`API: ${endpoint.charAt(0).toUpperCase() + endpoint.slice(1)} Postings`, async () => { + await testEndpoint(context, endpoint); + }); + }); +}); diff --git a/tests/api_part_time.test.ts b/tests/api_part_time.test.ts new file mode 100644 index 0000000..7642b1a --- /dev/null +++ b/tests/api_part_time.test.ts @@ -0,0 +1,77 @@ +import { test, expect, APIRequestContext } from '@playwright/test'; +import { config } from './config'; + +const baseURL = `${config.BASE_URL}/api/job-posting`; + +// Function to validate job properties +const validateJobProperties = (job: any) => { + expect(job).toHaveProperty('_id'); + // expect(job).toHaveProperty('jobTitle'); + // expect(job).toHaveProperty('datePosted'); + // expect(job).toHaveProperty('hiringOrganization'); + // expect(job).toHaveProperty('streetAddress'); + // expect(job).toHaveProperty('addressLocality'); + // expect(job).toHaveProperty('addressRegion'); + // expect(job).toHaveProperty('minCompValue'); + // expect(job).toHaveProperty('maxCompValue'); + // expect(job).toHaveProperty('compTimeUnit'); + // expect(job).toHaveProperty('workHours'); + // expect(job).toHaveProperty('specialCommitments'); + // expect(job).toHaveProperty('email'); + // expect(job).toHaveProperty('jobPageId'); + // expect(job).toHaveProperty('employmentType'); + // expect(job).toHaveProperty('employmentSubType'); + // expect(job).toHaveProperty('startTime'); + // expect(job).toHaveProperty('benefits'); + // expect(job).toHaveProperty('vacancies'); + // expect(job).toHaveProperty('verified'); + // expect(job).toHaveProperty('validThrough'); + // expect(job).toHaveProperty('description'); + // expect(job).toHaveProperty('site1'); + // expect(job).toHaveProperty('site2'); + // expect(job).toHaveProperty('site3'); + // expect(job).toHaveProperty('site4'); + // expect(job).toHaveProperty('__v'); + // expect(job).toHaveProperty('sent'); +}; + +// Function to test the endpoints +const testEndpoint = async (context: APIRequestContext, endpoint: string) => { + const response = await context.get(`${baseURL}/${endpoint}?et=pt&page_num=1`); + expect(response.status()).toBe(200); + const data = await response.json(); + + for (const joblist in data) { + for (const job of data[joblist]) { + validateJobProperties(job); + } + expect(data[joblist].length).toBeGreaterThan(0); + } +}; + +// Test for each endpoint +test.describe('API Job Postings', () => { + let context: APIRequestContext; + + test.beforeAll(async ({ playwright }) => { + context = await playwright.request.newContext(); + }); + + test.afterAll(async () => { + await context.dispose(); + }); + + const endpoints = [ + 'newcomers', + 'disabled', + 'indigenous', + 'students', + // 'asylum-refugees', + ]; + + endpoints.forEach(endpoint => { + test(`API: ${endpoint.charAt(0).toUpperCase() + endpoint.slice(1)} Postings`, async () => { + await testEndpoint(context, endpoint); + }); + }); +}); diff --git a/tests/api_sent_emails.test.ts b/tests/api_sent_emails.test.ts new file mode 100644 index 0000000..ce7b365 --- /dev/null +++ b/tests/api_sent_emails.test.ts @@ -0,0 +1,9 @@ +import { test, expect } from '@playwright/test'; +import { config } from './config'; + +test('API: Email Sent', async ({ request }) => { + const response = await request.get( + `${config.BASE_URL}/api/job-posting/email-sent?sort=-1` + ); + expect(response.status()).toBe(200); +}); diff --git a/tests/config.ts b/tests/config.ts new file mode 100644 index 0000000..c5a9c0d --- /dev/null +++ b/tests/config.ts @@ -0,0 +1,4 @@ +export const config = { + // BASE_URL: 'http://localhost:3000', // Local development use: npm run dev + BASE_URL: 'https://job-bank-admin.vercel.app', +}; diff --git a/e2e/job_posting.test.ts b/tests/job_posting.test.ts similarity index 94% rename from e2e/job_posting.test.ts rename to tests/job_posting.test.ts index d1ed294..bb2b57e 100644 --- a/e2e/job_posting.test.ts +++ b/tests/job_posting.test.ts @@ -36,6 +36,11 @@ test('Create Job Posting', async ({ browser, page }) => { try { await login(page); + + await page.waitForTimeout(2000); + await page.reload(); + await page.waitForTimeout(2000); + await page.getByRole('button', { name: 'Add New Job Posting' }).click(); await page.waitForTimeout(2000); @@ -76,7 +81,7 @@ test('Create Job Posting', async ({ browser, page }) => { await page .locator('div') - .filter({ hasText: new RegExp(`^${browserType} TestEditDelete$`) }) + .filter({ hasText: new RegExp(`^Pending${browserType} TestEditDelete$`) }) .getByRole('link') .click(); await page.waitForTimeout(2000); @@ -163,9 +168,13 @@ test('Edit Job Posting', async ({ browser, page }) => { try { await login(page); + await page.waitForTimeout(2000); + await page.reload(); + await page.waitForTimeout(2000); + await page .locator('div') - .filter({ hasText: new RegExp(`^${browserType} TestEditDelete$`) }) + .filter({ hasText: new RegExp(`^Pending${browserType} TestEditDelete$`) }) .getByRole('link') .click(); @@ -213,7 +222,7 @@ test('Edit Job Posting', async ({ browser, page }) => { await page .locator('div') .filter({ - hasText: new RegExp(`^${browserType} Updated TestEditDelete$`), + hasText: new RegExp(`^Pending${browserType} Updated TestEditDelete$`), }) .getByRole('link') .click(); @@ -291,12 +300,16 @@ test('Delete Job Posting', async ({ browser, page }) => { try { await login(page); + await page.waitForTimeout(2000); + await page.reload(); + await page.waitForTimeout(2000); + // Find the job to delete based on the browser type const jobTitle = `${browser.browserType().name()} Updated Test`; await page .locator('div') - .filter({ hasText: new RegExp(`^${jobTitle}EditDelete$`) }) + .filter({ hasText: new RegExp(`^Pending${jobTitle}EditDelete$`) }) .getByLabel('Delete job posting') .click(); diff --git a/tests/logout.test.ts b/tests/logout.test.ts new file mode 100644 index 0000000..bbd9b7c --- /dev/null +++ b/tests/logout.test.ts @@ -0,0 +1,27 @@ +import { test, expect } from '@playwright/test'; +import { config } from './config'; + +test('Logout after login', async ({ browser }) => { + const context = await browser.newContext(); + const page = await context.newPage(); + + try { + await page.goto(`${config.BASE_URL}/`); + + await page.getByRole('link', { name: 'Login / Sign Up' }).click(); + + await page.getByLabel('Email address*').fill('cboilley@my.bcit.ca'); + await page.getByLabel('Password*').fill('Bcit@123456'); + + await page.getByRole('button', { name: 'Continue' }).click(); + + await page.getByRole('link', { name: 'Logout' }).click(); + await expect(page).toHaveURL(`${config.BASE_URL}`); + console.log('Logout passed'); + } catch (error) { + console.log('Logout failed'); + console.error(error); + } finally { + await context.close(); + } +}); diff --git a/tests/signup.test.ts b/tests/signup.test.ts new file mode 100644 index 0000000..602fb97 --- /dev/null +++ b/tests/signup.test.ts @@ -0,0 +1,29 @@ +// import { config } from './config'; +// import { test, chromium } from '@playwright/test'; + +// test('Signup with existing email', async () => { +// const browser = await chromium.launch(); +// const page = await browser.newPage(); + +// try { +// await page.goto(`${config.BASE_URL}/auth`); + +// await page.getByLabel('Email address*').click(); +// await page.getByLabel('Email address*').fill('cboilley@my.bcit.ca'); + +// await page.getByLabel('Password*').click(); +// await page.getByLabel('Password*').fill('Bcit@123456'); + +// const signupButton = await page.$('button:has-text("Signup")'); +// await signupButton?.click(); +// await page.waitForSelector('text="User already registered"', { +// state: 'visible', +// }); +// console.error('Signup test with existing email passed'); +// } catch (error) { +// console.log('Signup test with existing email failed'); +// throw error; +// } finally { +// await browser.close(); +// } +// });