diff --git a/enatega-multivendor-web/__tests__/package-lock.json b/enatega-multivendor-web/__tests__/package-lock.json new file mode 100644 index 00000000..3307d357 --- /dev/null +++ b/enatega-multivendor-web/__tests__/package-lock.json @@ -0,0 +1,97 @@ +{ + "name": "__tests__", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "__tests__", + "version": "1.0.0", + "license": "ISC", + "devDependencies": { + "@playwright/test": "^1.45.3", + "@types/node": "^20.14.11" + } + }, + "node_modules/@playwright/test": { + "version": "1.45.3", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.45.3.tgz", + "integrity": "sha512-UKF4XsBfy+u3MFWEH44hva1Q8Da28G6RFtR2+5saw+jgAFQV5yYnB1fu68Mz7fO+5GJF3wgwAIs0UelU8TxFrA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.45.3" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@types/node": { + "version": "20.14.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.11.tgz", + "integrity": "sha512-kprQpL8MMeszbz6ojB5/tU8PLN4kesnN8Gjzw349rDlNgsSzg90lAVj3llK99Dh7JON+t9AuscPPFW6mPbTnSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/playwright": { + "version": "1.45.3", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.45.3.tgz", + "integrity": "sha512-QhVaS+lpluxCaioejDZ95l4Y4jSFCsBvl2UZkpeXlzxmqS+aABr5c82YmfMHrL6x27nvrvykJAFpkzT2eWdJww==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.45.3" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.45.3", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.45.3.tgz", + "integrity": "sha512-+ym0jNbcjikaOwwSZycFbwkWgfruWvYlJfThKYAlImbxUgdWFO2oW70ojPm4OpE4t6TAo2FY/smM+hpVTtkhDA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true, + "license": "MIT" + } + } +} diff --git a/enatega-multivendor-web/__tests__/package.json b/enatega-multivendor-web/__tests__/package.json new file mode 100644 index 00000000..03054488 --- /dev/null +++ b/enatega-multivendor-web/__tests__/package.json @@ -0,0 +1,14 @@ +{ + "name": "__tests__", + "version": "1.0.0", + "main": "index.js", + "scripts": {}, + "keywords": [], + "author": "", + "license": "ISC", + "description": "", + "devDependencies": { + "@playwright/test": "^1.45.3", + "@types/node": "^20.14.11" + } +} diff --git a/enatega-multivendor-web/__tests__/playwright-report/index.html b/enatega-multivendor-web/__tests__/playwright-report/index.html new file mode 100644 index 00000000..dc60ec26 --- /dev/null +++ b/enatega-multivendor-web/__tests__/playwright-report/index.html @@ -0,0 +1,68 @@ + + + + + + + + + Playwright Test Report + + + + +
+ + + \ No newline at end of file diff --git a/enatega-multivendor-web/__tests__/playwright.config.ts b/enatega-multivendor-web/__tests__/playwright.config.ts new file mode 100644 index 00000000..4a501ea1 --- /dev/null +++ b/enatega-multivendor-web/__tests__/playwright.config.ts @@ -0,0 +1,78 @@ +import { defineConfig, devices } from '@playwright/test'; + +/** + * Read environment variables from file. + * https://github.com/motdotla/dotenv + */ +// import dotenv from 'dotenv'; +// dotenv.config({ path: path.resolve(__dirname, '.env') }); + +/** + * See https://playwright.dev/docs/test-configuration. + */ +export default defineConfig({ + testDir: './tests', + /* Run tests in files in parallel */ + fullyParallel: true, + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: !!process.env.CI, + /* Retry on CI only */ + retries: process.env.CI ? 2 : 0, + /* Opt out of parallel tests on CI. */ + workers: process.env.CI ? 1 : undefined, + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: 'html', + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + /* Base URL to use in actions like `await page.goto('/')`. */ + // baseURL: 'http://127.0.0.1:3000', + + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: 'on-first-retry', + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + + { + name: 'firefox', + use: { ...devices['Desktop Firefox'] }, + }, + + { + name: 'webkit', + use: { ...devices['Desktop Safari'] }, + }, + + /* Test against mobile viewports. */ + // { + // name: 'Mobile Chrome', + // use: { ...devices['Pixel 5'] }, + // }, + // { + // name: 'Mobile Safari', + // use: { ...devices['iPhone 12'] }, + // }, + + /* Test against branded browsers. */ + // { + // name: 'Microsoft Edge', + // use: { ...devices['Desktop Edge'], channel: 'msedge' }, + // }, + // { + // name: 'Google Chrome', + // use: { ...devices['Desktop Chrome'], channel: 'chrome' }, + // }, + ], + + /* Run your local dev server before starting the tests */ + // webServer: { + // command: 'npm run start', + // url: 'http://127.0.0.1:3000', + // reuseExistingServer: !process.env.CI, + // }, +}); diff --git a/enatega-multivendor-web/__tests__/tests-examples/demo-todo-app.spec.ts b/enatega-multivendor-web/__tests__/tests-examples/demo-todo-app.spec.ts new file mode 100644 index 00000000..8641cb5f --- /dev/null +++ b/enatega-multivendor-web/__tests__/tests-examples/demo-todo-app.spec.ts @@ -0,0 +1,437 @@ +import { test, expect, type Page } from '@playwright/test'; + +test.beforeEach(async ({ page }) => { + await page.goto('https://demo.playwright.dev/todomvc'); +}); + +const TODO_ITEMS = [ + 'buy some cheese', + 'feed the cat', + 'book a doctors appointment' +] as const; + +test.describe('New Todo', () => { + test('should allow me to add todo items', async ({ page }) => { + // create a new todo locator + const newTodo = page.getByPlaceholder('What needs to be done?'); + + // Create 1st todo. + await newTodo.fill(TODO_ITEMS[0]); + await newTodo.press('Enter'); + + // Make sure the list only has one todo item. + await expect(page.getByTestId('todo-title')).toHaveText([ + TODO_ITEMS[0] + ]); + + // Create 2nd todo. + await newTodo.fill(TODO_ITEMS[1]); + await newTodo.press('Enter'); + + // Make sure the list now has two todo items. + await expect(page.getByTestId('todo-title')).toHaveText([ + TODO_ITEMS[0], + TODO_ITEMS[1] + ]); + + await checkNumberOfTodosInLocalStorage(page, 2); + }); + + test('should clear text input field when an item is added', async ({ page }) => { + // create a new todo locator + const newTodo = page.getByPlaceholder('What needs to be done?'); + + // Create one todo item. + await newTodo.fill(TODO_ITEMS[0]); + await newTodo.press('Enter'); + + // Check that input is empty. + await expect(newTodo).toBeEmpty(); + await checkNumberOfTodosInLocalStorage(page, 1); + }); + + test('should append new items to the bottom of the list', async ({ page }) => { + // Create 3 items. + await createDefaultTodos(page); + + // create a todo count locator + const todoCount = page.getByTestId('todo-count') + + // Check test using different methods. + await expect(page.getByText('3 items left')).toBeVisible(); + await expect(todoCount).toHaveText('3 items left'); + await expect(todoCount).toContainText('3'); + await expect(todoCount).toHaveText(/3/); + + // Check all items in one call. + await expect(page.getByTestId('todo-title')).toHaveText(TODO_ITEMS); + await checkNumberOfTodosInLocalStorage(page, 3); + }); +}); + +test.describe('Mark all as completed', () => { + test.beforeEach(async ({ page }) => { + await createDefaultTodos(page); + await checkNumberOfTodosInLocalStorage(page, 3); + }); + + test.afterEach(async ({ page }) => { + await checkNumberOfTodosInLocalStorage(page, 3); + }); + + test('should allow me to mark all items as completed', async ({ page }) => { + // Complete all todos. + await page.getByLabel('Mark all as complete').check(); + + // Ensure all todos have 'completed' class. + await expect(page.getByTestId('todo-item')).toHaveClass(['completed', 'completed', 'completed']); + await checkNumberOfCompletedTodosInLocalStorage(page, 3); + }); + + test('should allow me to clear the complete state of all items', async ({ page }) => { + const toggleAll = page.getByLabel('Mark all as complete'); + // Check and then immediately uncheck. + await toggleAll.check(); + await toggleAll.uncheck(); + + // Should be no completed classes. + await expect(page.getByTestId('todo-item')).toHaveClass(['', '', '']); + }); + + test('complete all checkbox should update state when items are completed / cleared', async ({ page }) => { + const toggleAll = page.getByLabel('Mark all as complete'); + await toggleAll.check(); + await expect(toggleAll).toBeChecked(); + await checkNumberOfCompletedTodosInLocalStorage(page, 3); + + // Uncheck first todo. + const firstTodo = page.getByTestId('todo-item').nth(0); + await firstTodo.getByRole('checkbox').uncheck(); + + // Reuse toggleAll locator and make sure its not checked. + await expect(toggleAll).not.toBeChecked(); + + await firstTodo.getByRole('checkbox').check(); + await checkNumberOfCompletedTodosInLocalStorage(page, 3); + + // Assert the toggle all is checked again. + await expect(toggleAll).toBeChecked(); + }); +}); + +test.describe('Item', () => { + + test('should allow me to mark items as complete', async ({ page }) => { + // create a new todo locator + const newTodo = page.getByPlaceholder('What needs to be done?'); + + // Create two items. + for (const item of TODO_ITEMS.slice(0, 2)) { + await newTodo.fill(item); + await newTodo.press('Enter'); + } + + // Check first item. + const firstTodo = page.getByTestId('todo-item').nth(0); + await firstTodo.getByRole('checkbox').check(); + await expect(firstTodo).toHaveClass('completed'); + + // Check second item. + const secondTodo = page.getByTestId('todo-item').nth(1); + await expect(secondTodo).not.toHaveClass('completed'); + await secondTodo.getByRole('checkbox').check(); + + // Assert completed class. + await expect(firstTodo).toHaveClass('completed'); + await expect(secondTodo).toHaveClass('completed'); + }); + + test('should allow me to un-mark items as complete', async ({ page }) => { + // create a new todo locator + const newTodo = page.getByPlaceholder('What needs to be done?'); + + // Create two items. + for (const item of TODO_ITEMS.slice(0, 2)) { + await newTodo.fill(item); + await newTodo.press('Enter'); + } + + const firstTodo = page.getByTestId('todo-item').nth(0); + const secondTodo = page.getByTestId('todo-item').nth(1); + const firstTodoCheckbox = firstTodo.getByRole('checkbox'); + + await firstTodoCheckbox.check(); + await expect(firstTodo).toHaveClass('completed'); + await expect(secondTodo).not.toHaveClass('completed'); + await checkNumberOfCompletedTodosInLocalStorage(page, 1); + + await firstTodoCheckbox.uncheck(); + await expect(firstTodo).not.toHaveClass('completed'); + await expect(secondTodo).not.toHaveClass('completed'); + await checkNumberOfCompletedTodosInLocalStorage(page, 0); + }); + + test('should allow me to edit an item', async ({ page }) => { + await createDefaultTodos(page); + + const todoItems = page.getByTestId('todo-item'); + const secondTodo = todoItems.nth(1); + await secondTodo.dblclick(); + await expect(secondTodo.getByRole('textbox', { name: 'Edit' })).toHaveValue(TODO_ITEMS[1]); + await secondTodo.getByRole('textbox', { name: 'Edit' }).fill('buy some sausages'); + await secondTodo.getByRole('textbox', { name: 'Edit' }).press('Enter'); + + // Explicitly assert the new text value. + await expect(todoItems).toHaveText([ + TODO_ITEMS[0], + 'buy some sausages', + TODO_ITEMS[2] + ]); + await checkTodosInLocalStorage(page, 'buy some sausages'); + }); +}); + +test.describe('Editing', () => { + test.beforeEach(async ({ page }) => { + await createDefaultTodos(page); + await checkNumberOfTodosInLocalStorage(page, 3); + }); + + test('should hide other controls when editing', async ({ page }) => { + const todoItem = page.getByTestId('todo-item').nth(1); + await todoItem.dblclick(); + await expect(todoItem.getByRole('checkbox')).not.toBeVisible(); + await expect(todoItem.locator('label', { + hasText: TODO_ITEMS[1], + })).not.toBeVisible(); + await checkNumberOfTodosInLocalStorage(page, 3); + }); + + test('should save edits on blur', async ({ page }) => { + const todoItems = page.getByTestId('todo-item'); + await todoItems.nth(1).dblclick(); + await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('buy some sausages'); + await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).dispatchEvent('blur'); + + await expect(todoItems).toHaveText([ + TODO_ITEMS[0], + 'buy some sausages', + TODO_ITEMS[2], + ]); + await checkTodosInLocalStorage(page, 'buy some sausages'); + }); + + test('should trim entered text', async ({ page }) => { + const todoItems = page.getByTestId('todo-item'); + await todoItems.nth(1).dblclick(); + await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill(' buy some sausages '); + await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Enter'); + + await expect(todoItems).toHaveText([ + TODO_ITEMS[0], + 'buy some sausages', + TODO_ITEMS[2], + ]); + await checkTodosInLocalStorage(page, 'buy some sausages'); + }); + + test('should remove the item if an empty text string was entered', async ({ page }) => { + const todoItems = page.getByTestId('todo-item'); + await todoItems.nth(1).dblclick(); + await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill(''); + await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Enter'); + + await expect(todoItems).toHaveText([ + TODO_ITEMS[0], + TODO_ITEMS[2], + ]); + }); + + test('should cancel edits on escape', async ({ page }) => { + const todoItems = page.getByTestId('todo-item'); + await todoItems.nth(1).dblclick(); + await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('buy some sausages'); + await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Escape'); + await expect(todoItems).toHaveText(TODO_ITEMS); + }); +}); + +test.describe('Counter', () => { + test('should display the current number of todo items', async ({ page }) => { + // create a new todo locator + const newTodo = page.getByPlaceholder('What needs to be done?'); + + // create a todo count locator + const todoCount = page.getByTestId('todo-count') + + await newTodo.fill(TODO_ITEMS[0]); + await newTodo.press('Enter'); + + await expect(todoCount).toContainText('1'); + + await newTodo.fill(TODO_ITEMS[1]); + await newTodo.press('Enter'); + await expect(todoCount).toContainText('2'); + + await checkNumberOfTodosInLocalStorage(page, 2); + }); +}); + +test.describe('Clear completed button', () => { + test.beforeEach(async ({ page }) => { + await createDefaultTodos(page); + }); + + test('should display the correct text', async ({ page }) => { + await page.locator('.todo-list li .toggle').first().check(); + await expect(page.getByRole('button', { name: 'Clear completed' })).toBeVisible(); + }); + + test('should remove completed items when clicked', async ({ page }) => { + const todoItems = page.getByTestId('todo-item'); + await todoItems.nth(1).getByRole('checkbox').check(); + await page.getByRole('button', { name: 'Clear completed' }).click(); + await expect(todoItems).toHaveCount(2); + await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[2]]); + }); + + test('should be hidden when there are no items that are completed', async ({ page }) => { + await page.locator('.todo-list li .toggle').first().check(); + await page.getByRole('button', { name: 'Clear completed' }).click(); + await expect(page.getByRole('button', { name: 'Clear completed' })).toBeHidden(); + }); +}); + +test.describe('Persistence', () => { + test('should persist its data', async ({ page }) => { + // create a new todo locator + const newTodo = page.getByPlaceholder('What needs to be done?'); + + for (const item of TODO_ITEMS.slice(0, 2)) { + await newTodo.fill(item); + await newTodo.press('Enter'); + } + + const todoItems = page.getByTestId('todo-item'); + const firstTodoCheck = todoItems.nth(0).getByRole('checkbox'); + await firstTodoCheck.check(); + await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[1]]); + await expect(firstTodoCheck).toBeChecked(); + await expect(todoItems).toHaveClass(['completed', '']); + + // Ensure there is 1 completed item. + await checkNumberOfCompletedTodosInLocalStorage(page, 1); + + // Now reload. + await page.reload(); + await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[1]]); + await expect(firstTodoCheck).toBeChecked(); + await expect(todoItems).toHaveClass(['completed', '']); + }); +}); + +test.describe('Routing', () => { + test.beforeEach(async ({ page }) => { + await createDefaultTodos(page); + // make sure the app had a chance to save updated todos in storage + // before navigating to a new view, otherwise the items can get lost :( + // in some frameworks like Durandal + await checkTodosInLocalStorage(page, TODO_ITEMS[0]); + }); + + test('should allow me to display active items', async ({ page }) => { + const todoItem = page.getByTestId('todo-item'); + await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check(); + + await checkNumberOfCompletedTodosInLocalStorage(page, 1); + await page.getByRole('link', { name: 'Active' }).click(); + await expect(todoItem).toHaveCount(2); + await expect(todoItem).toHaveText([TODO_ITEMS[0], TODO_ITEMS[2]]); + }); + + test('should respect the back button', async ({ page }) => { + const todoItem = page.getByTestId('todo-item'); + await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check(); + + await checkNumberOfCompletedTodosInLocalStorage(page, 1); + + await test.step('Showing all items', async () => { + await page.getByRole('link', { name: 'All' }).click(); + await expect(todoItem).toHaveCount(3); + }); + + await test.step('Showing active items', async () => { + await page.getByRole('link', { name: 'Active' }).click(); + }); + + await test.step('Showing completed items', async () => { + await page.getByRole('link', { name: 'Completed' }).click(); + }); + + await expect(todoItem).toHaveCount(1); + await page.goBack(); + await expect(todoItem).toHaveCount(2); + await page.goBack(); + await expect(todoItem).toHaveCount(3); + }); + + test('should allow me to display completed items', async ({ page }) => { + await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check(); + await checkNumberOfCompletedTodosInLocalStorage(page, 1); + await page.getByRole('link', { name: 'Completed' }).click(); + await expect(page.getByTestId('todo-item')).toHaveCount(1); + }); + + test('should allow me to display all items', async ({ page }) => { + await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check(); + await checkNumberOfCompletedTodosInLocalStorage(page, 1); + await page.getByRole('link', { name: 'Active' }).click(); + await page.getByRole('link', { name: 'Completed' }).click(); + await page.getByRole('link', { name: 'All' }).click(); + await expect(page.getByTestId('todo-item')).toHaveCount(3); + }); + + test('should highlight the currently applied filter', async ({ page }) => { + await expect(page.getByRole('link', { name: 'All' })).toHaveClass('selected'); + + //create locators for active and completed links + const activeLink = page.getByRole('link', { name: 'Active' }); + const completedLink = page.getByRole('link', { name: 'Completed' }); + await activeLink.click(); + + // Page change - active items. + await expect(activeLink).toHaveClass('selected'); + await completedLink.click(); + + // Page change - completed items. + await expect(completedLink).toHaveClass('selected'); + }); +}); + +async function createDefaultTodos(page: Page) { + // create a new todo locator + const newTodo = page.getByPlaceholder('What needs to be done?'); + + for (const item of TODO_ITEMS) { + await newTodo.fill(item); + await newTodo.press('Enter'); + } +} + +async function checkNumberOfTodosInLocalStorage(page: Page, expected: number) { + return await page.waitForFunction(e => { + return JSON.parse(localStorage['react-todos']).length === e; + }, expected); +} + +async function checkNumberOfCompletedTodosInLocalStorage(page: Page, expected: number) { + return await page.waitForFunction(e => { + return JSON.parse(localStorage['react-todos']).filter((todo: any) => todo.completed).length === e; + }, expected); +} + +async function checkTodosInLocalStorage(page: Page, title: string) { + return await page.waitForFunction(t => { + return JSON.parse(localStorage['react-todos']).map((todo: any) => todo.title).includes(t); + }, title); +} diff --git a/enatega-multivendor-web/__tests__/tests/example.spec.ts b/enatega-multivendor-web/__tests__/tests/example.spec.ts new file mode 100644 index 00000000..54a906a4 --- /dev/null +++ b/enatega-multivendor-web/__tests__/tests/example.spec.ts @@ -0,0 +1,18 @@ +import { test, expect } from '@playwright/test'; + +test('has title', async ({ page }) => { + await page.goto('https://playwright.dev/'); + + // Expect a title "to contain" a substring. + await expect(page).toHaveTitle(/Playwright/); +}); + +test('get started link', async ({ page }) => { + await page.goto('https://playwright.dev/'); + + // Click the get started link. + await page.getByRole('link', { name: 'Get started' }).click(); + + // Expects page to have a heading with the name of Installation. + await expect(page.getByRole('heading', { name: 'Installation' })).toBeVisible(); +}); diff --git a/enatega-multivendor-web/__tests__/tests/home.spec.ts b/enatega-multivendor-web/__tests__/tests/home.spec.ts new file mode 100644 index 00000000..8c1c5cb3 --- /dev/null +++ b/enatega-multivendor-web/__tests__/tests/home.spec.ts @@ -0,0 +1,97 @@ +import { test, expect } from '@playwright/test'; + +test('check specific elements existence', async ({ page }) => { + await page.goto('http://localhost:3000/#/'); + + // Check if the first image with alt "fruits2" is present +// await expect(page.locator('img[alt="fruits2"]')).toBeVisible(); + + // Check if the banner images are present + await expect(page.locator('img[alt="banner2"]')).toBeVisible(); + await expect(page.locator('img[alt="banner1"]')).toBeVisible(); + + // Check if the text "Put us in your pocket" is present + await expect(page.locator('p:has-text("Put us in your pocket")')).toBeVisible(); + + // Check if the "Ios Store" button is present + await expect(page. getByRole('button', { name: 'playstore Ios Store' })).toBeVisible(); + + // Check if the "Play Store" button is present + await expect(page.getByRole('button', { name: 'appstore Play Store' }).nth(1)).toBeVisible(); +}); + +test('check presence of elements', async ({ page }) => { + await page.goto('http://localhost:3000/#/'); + + // Check for the presence of the "FEATURES" text + const featuresText = await page.locator('p:has-text("FEATURES")'); + await expect(featuresText).toBeVisible(); + + // Check for the presence of Rider App section + await expect(page.locator('h5:has-text("Rider App")')).toBeVisible(); + await expect(page.locator('img[alt="apps"][src="/static/media/rider-app.5d2d755f.png"]')).toBeVisible(); + await expect(page.getByText('• Finding address using GPS').first()).toBeVisible(); + await expect(page.getByText('• Zones functionality for').first()).toBeVisible(); + await expect(page.getByRole('button', { name: 'appstore Play Store' }).nth(1)).toBeVisible(); + await expect(page.getByRole('button', { name: 'playstore Ios Store' })).toBeVisible(); + + // Check for the presence of Restaurant App section + await expect(page.locator('h5:has-text("Restaurant App")')).toBeVisible(); + await expect(page.locator('img[alt="apps"][src="/static/media/restaurant-app.7c194106.png"]')).toBeVisible(); + await expect(page.locator('p:has-text("Multiple Restaurant adding feature")')).toBeVisible(); + await expect(page.locator('p:has-text("Real-time order receiving updates")')).toBeVisible(); + await expect(page.getByRole('button', { name: 'appstore Play Store' }).nth(1)).toBeVisible(); + await expect(page.getByRole('button', { name: 'playstore Ios Store' })).toBeVisible(); + + + // Check for the presence of Customer App section + await expect(page.locator('h5:has-text("Customer App")')).toBeVisible(); + await expect(page.locator('img[alt="apps"][src="/static/media/cust-app.381e11da.png"]')).toBeVisible(); + await expect(page.locator('p:has-text("Different sections feature for promoting restaurants")')).toBeVisible(); + await expect(page.locator('p:has-text("Previous order history and adding favorite restaurants")')).toBeVisible(); + await expect(page.getByRole('button', { name: 'appstore Play Store' }).nth(1)).toBeVisible(); + await expect(page.getByRole('button', { name: 'playstore Ios Store' })).toBeVisible(); + + // Check for the presence of Admin Dashboard section + await expect(page.locator('h5:has-text("Admin Dashboard")')).toBeVisible(); + await expect(page.locator('img[alt="apps"][src="/static/media/dashboard.1da8f08f.png"]')).toBeVisible(); + await expect(page.getByText('• Finding address using GPS').first()).toBeVisible(); + await expect(page.getByText('• Zones functionality for').first()).toBeVisible(); + await expect(page.getByRole('button', { name: 'Demo Demo' }).first()).toBeVisible(); + + // Check for the presence of Product Page section + await expect(page.locator('h5:has-text("Product Page")')).toBeVisible(); + await expect(page.locator('img[alt="apps"][src="/static/media/webapp.e31445e6.png"]')).toBeVisible(); + await expect(page.locator('p:has-text("Our delivery management system is designed for the future.")')).toBeVisible(); + await expect(page.locator('p:has-text("Built on community-driven principles.")')).toBeVisible(); + await expect(page.getByRole('button', { name: 'Demo Demo' }).first()).toBeVisible(); + }); + + + test('check presence of footer elements', async ({ page }) => { + await page.goto('http://localhost:3000/#/'); + + // Check for the presence of "Enatega" header and description + const header = page.locator('h4:has-text("Enatega")'); + const description = page.locator('p:has-text("Enatega is an open-source delivery management platform for the future.")'); + + await expect(header).toBeVisible(); + await expect(description).toBeVisible(); + + // Check for the presence of "Links" section + await expect(page.locator('p:has-text("Links")')).toBeVisible(); + await expect(page.locator('a:has-text("Home")')).toBeVisible(); + await expect(page.locator('a:has-text("Privacy Policy")')).toBeVisible(); + await expect(page.locator('a:has-text("Terms and Conditions")')).toBeVisible(); + + // Check for the presence of social media icons in the "Follow Us" section + await expect(page.locator('svg[data-testid="FacebookIcon"]')).toBeVisible(); + await expect(page.locator('svg[data-testid="TwitterIcon"]')).toBeVisible(); + await expect(page.locator('svg[data-testid="InstagramIcon"]')).toBeVisible(); + await expect(page.locator('svg[data-testid="LinkedInIcon"]')).toBeVisible(); + await expect(page.locator('svg[data-testid="GitHubIcon"]')).toBeVisible(); + + // Check for the presence of "Powered By" section with "ninjascode" + await expect(page.locator('p:has-text("Powered By")')).toBeVisible(); + await expect(page.locator('p:has-text("ninjascode")')).toBeVisible(); + }); \ No newline at end of file diff --git a/enatega-multivendor-web/__tests__/tests/login.spec.ts b/enatega-multivendor-web/__tests__/tests/login.spec.ts new file mode 100644 index 00000000..82a51136 --- /dev/null +++ b/enatega-multivendor-web/__tests__/tests/login.spec.ts @@ -0,0 +1,88 @@ +import { test, expect } from '@playwright/test'; + + + test('has title', async ({ page }) => { + await page.goto('http://localhost:3000/#/new-login'); + + await expect(page).toHaveTitle(/Enatega-Food Delivery/); + }); + + + test('Check for the existence of specific elements', async ({ page }) => { + await page.goto('http://localhost:3000/#/login-email'); + + const expectedRedirectUrl = 'http://localhost:3000/#/'; + const formHeadingText = 'Sign in with your email'; + const passwordInstructionText = 'Type your password'; + const forgotPasswordText = 'Forgot your password?'; + const continueButtonText = 'CONTINUE'; + await expect(page.getByText(formHeadingText)).toBeVisible(); + + // Check for the password instruction + await expect(page.getByText(passwordInstructionText)).toBeVisible(); + + // Check for the Email label + await expect(page.getByLabel('Email')).toBeVisible(); + + // Check for the Password label + await expect(page.getByLabel('Password', { exact: true })).toBeVisible(); + + // Check for the Forgot your password link text + await expect(page.getByText(forgotPasswordText)).toBeVisible(); + + // Check for the CONTINUE button text + await expect(page.getByText(continueButtonText)).toBeVisible(); + + // Click the CONTINUE button + await page.getByText(continueButtonText).click(); + + // Wait for the navigation to complete + await page.waitForNavigation(); + + // Check that the new URL is correct + await expect(page).toHaveURL(expectedRedirectUrl); + }); + + +test('Check for the existence of specific elements by text content and redirection', async ({ page }) => { + await page.goto('http://localhost:3000/#/login'); + + const expectedRedirectUrl = 'http://localhost:3000/#/new-login'; + const welcomeText = 'Welcome!'; + const signUpOrLogInText = 'Sign up or log in to continue'; + const continueWithGoogleText = 'CONTINUE WITH GOOGLE'; + const continueWithEmailText = 'CONTINUE WITH EMAIL'; + const termsAndConditionsText = 'Terms and Conditions'; + const privacyPolicyText = 'Privacy Policy'; + + + // Check for the welcome text + await expect(page.getByText(welcomeText)).toBeVisible(); + + // Check for the sign-up or log-in text + await expect(page.getByText(signUpOrLogInText)).toBeVisible(); + + // Check for the CONTINUE WITH GOOGLE button text + await expect(page.getByText(continueWithGoogleText)).toBeVisible(); + + // Check for the "or" text + await expect(page.getByText('or', { exact: true })).toBeVisible(); + + // Check for the CONTINUE WITH EMAIL button text + await expect(page.getByText(continueWithEmailText)).toBeVisible(); + + // Check for the Terms and Conditions link text + await expect(page.getByText(termsAndConditionsText)).toBeVisible(); + + // Check for the Privacy Policy link text + await expect(page.getByText(privacyPolicyText)).toBeVisible(); + + // Click the CONTINUE WITH EMAIL button + await page.getByText(continueWithEmailText).click(); + + // Wait for the navigation to complete + //await page.waitForNavigation(); + + // Check that the new URL is correct + await expect(page).toHaveURL(expectedRedirectUrl); +}); \ No newline at end of file diff --git a/enatega-multivendor-web/__tests__/tests/new-login.spec.ts b/enatega-multivendor-web/__tests__/tests/new-login.spec.ts new file mode 100644 index 00000000..19542d59 --- /dev/null +++ b/enatega-multivendor-web/__tests__/tests/new-login.spec.ts @@ -0,0 +1,8 @@ +import { test, expect } from '@playwright/test'; + + +test('has title', async ({ page }) => { + await page.goto('http://localhost:3000/#/new-login'); + + await expect(page).toHaveTitle(/Enatega-Food Delivery/); + }); \ No newline at end of file diff --git a/enatega-multivendor-web/__tests__/tests/restaurant-list.spec.ts b/enatega-multivendor-web/__tests__/tests/restaurant-list.spec.ts new file mode 100644 index 00000000..81af8f9e --- /dev/null +++ b/enatega-multivendor-web/__tests__/tests/restaurant-list.spec.ts @@ -0,0 +1,79 @@ +import { test, expect } from '@playwright/test'; + +test('Check for All Restaurants', async ({ page }) => { + await page.goto('http://localhost:3000/#/restaurant-list'); + + const inputLocator = page.locator('input[placeholder="Search for restaurant and cuisines"]'); // Replace with the actual placeholder text + + // input field is visible on the page + await expect(inputLocator).toBeVisible(); + + // input field is enabled + await expect(inputLocator).toBeEnabled(); + + // Check if the heading exists + const targetHeader = 'All Restaurants' + await expect(page.getByText(targetHeader).first()).toBeVisible(); + + // Check if the heading exists + const TimeText = '20' + await expect(page.getByText(TimeText).first()).toBeVisible(); + + // const svgElement = await page.$('//svg[contains(@class, "MuiSvgIcon-root") and contains(@class, "MuiSvgIcon-fontSizeSmall") and contains(@class, "makeStyles-icon-481") and contains(@class, "css-ptiqhd-MuiSvgIcon-root") and @data-testid="FavoriteBorderIcon"]'); + // expect(svgElement).not.toBeNull(); + + + // const spanLocator = page.getByText(':has-text("Rice,Gravy,Soups")').first(); + // await expect(spanLocator).toBeVisible(); + + + // Check if the restaurant name exists + const Juiceheading = page.locator('h6:has-text("Juice Bar-East")'); + await expect(Juiceheading).toBeVisible(); + + const Chineseheading = page.locator('h6:has-text("Chinese Food-East")'); + await expect(Chineseheading).toBeVisible(); + + const IceCreameheading = page.locator('h6:has-text("Ice-Cream Shop-East")'); + await expect(IceCreameheading).toBeVisible(); + + const Italianheading = page.locator('h6:has-text("Italian Food-East")'); + await expect(Italianheading).toBeVisible(); + + const Probaheading = page.locator(':has-text("Proba")'); + await expect(Probaheading).toBeVisible(); + + const Popularheading = page.locator(':has-text("Popular this week")'); + await expect(Popularheading).toBeVisible(); + + }); + + + test('check presence of footer elements', async ({ page }) => { + await page.goto('http://localhost:3000/#/restaurant-list'); + + // Check for the presence of "Enatega" header and description + const header = page.locator('h4:has-text("Enatega")'); + const description = page.locator('p:has-text("Enatega is an open-source delivery management platform for the future.")'); + + await expect(header).toBeVisible(); + await expect(description).toBeVisible(); + + // Check for the presence of "Links" section + await expect(page.locator('p:has-text("Links")')).toBeVisible(); + await expect(page.locator('a:has-text("Home")')).toBeVisible(); + await expect(page.locator('a:has-text("Privacy Policy")')).toBeVisible(); + await expect(page.locator('a:has-text("Terms and Conditions")')).toBeVisible(); + + // Check for the presence of social media icons in the "Follow Us" section + await expect(page.locator('svg[data-testid="FacebookIcon"]')).toBeVisible(); + await expect(page.locator('svg[data-testid="TwitterIcon"]')).toBeVisible(); + await expect(page.locator('svg[data-testid="InstagramIcon"]')).toBeVisible(); + await expect(page.locator('svg[data-testid="LinkedInIcon"]')).toBeVisible(); + await expect(page.locator('svg[data-testid="GitHubIcon"]')).toBeVisible(); + + // Check for the presence of "Powered By" section with "ninjascode" + await expect(page.locator('p:has-text("Powered By")')).toBeVisible(); + await expect(page.locator('p:has-text("ninjascode")')).toBeVisible(); + }); + diff --git a/enatega-multivendor-web/__tests__/tests/restaurant.spec.ts b/enatega-multivendor-web/__tests__/tests/restaurant.spec.ts new file mode 100644 index 00000000..4ff34088 --- /dev/null +++ b/enatega-multivendor-web/__tests__/tests/restaurant.spec.ts @@ -0,0 +1,79 @@ +import { test, expect } from '@playwright/test'; + + +test('[/burger-shop-east] Check if main elements exist', async ({ page }) => { + await page.goto('http://localhost:3000/#/restaurant/burger-shop-east'); + + // Check for the parent container + const parentContainer = await page.locator('div.MuiBox-root.css-70qvj9'); + await expect(parentContainer).toBeVisible(); + + // Check for the child div + const childDiv = await parentContainer.locator('div.MuiBox-root.css-gmuwbf'); + await expect(childDiv).toBeVisible(); + + // Check for the image element + const imageElement = await childDiv.locator('img[src^="data:image/png;base64"]'); + await expect(imageElement).toBeVisible(); + + // Check for 'Your Cart' text + const cartText = await parentContainer.locator('p.MuiTypography-root.MuiTypography-body1'); + await expect(cartText).toHaveText('Your Cart'); + + // Check for 'Start adding items to your cart' text + const startText = await parentContainer.locator('h6.MuiTypography-root.MuiTypography-h6'); + await expect(startText).toHaveText('Start adding items to your cart'); + }); + + + test('[/burger-shop-east] Check header elements', async ({ page }) => { + await page.goto('http://localhost:3000/#/restaurant/burger-shop-east'); + + // Wait for the image container + const imageContainer = page.locator('div.makeStyles-imageContainer-6.MuiBox-root'); + await expect(imageContainer).toBeVisible({ timeout: 10000 }); + + // Check for the detail container + const detailContainer = imageContainer.locator('div.makeStyles-restaurantDetail-13.MuiBox-root'); + await expect(detailContainer).toBeVisible({ timeout: 10000 }); + + // Check for the restaurant title + const restaurantTitle = detailContainer.locator('text=Burger Shop-East'); + await expect(restaurantTitle).toBeVisible({ timeout: 10000 }); + + // Check for the button inside the image container + const buttonElement = imageContainer.locator('button.MuiButtonBase-root'); + await expect(buttonElement).toBeVisible({ timeout: 10000 }); + + // Check for the 'NEW' tag + const newTagElement = detailContainer.getByText('NEW'); + await expect(newTagElement).toBeVisible({ timeout: 10000 }); + + // Check for the rating icon and text + const ratingIcon = detailContainer.locator('svg[data-testid="StarSharpIcon"]'); + await expect(ratingIcon).toBeVisible({ timeout: 10000 }); + const currentRatingText = detailContainer.getByText('3'); + await expect(currentRatingText).toBeVisible({ timeout: 10000 }); + const totalRatingText = detailContainer.getByText('/5'); + await expect(totalRatingText).toBeVisible({ timeout: 10000 }); + + // Check for the delivery time text + const deliveryTimeText = detailContainer.getByText('Delivery 1 Minute'); + await expect(deliveryTimeText).toBeVisible({ timeout: 10000 }); + }); + + + test('[/burger-shop-east] Check navigation buttons', async ({ page }) => { + await page.goto('http://localhost:3000/#/restaurant/burger-shop-east'); + + // Wait for the container to be visible + const mainContainer = page.locator('div.MuiContainer-root.MuiContainer-maxWidthLg.css-1oqqzyl-MuiContainer-root'); + await expect(mainContainer).toBeVisible({ timeout: 10000 }); + + // Check visibility of each specific tab item by text + await expect(page.locator('h5').filter({ hasText: 'Burgers' })).toBeVisible(); + await expect(page.locator('h5').filter({ hasText: 'Burritos' })).toBeVisible(); + await expect(page.locator('h5').filter({ hasText: 'Fries' })).toBeVisible(); + await expect(page.locator('h5').filter({ hasText: 'Beverages' })).toBeVisible(); + await expect(page.locator('h5').filter({ hasText: 'Popular' })).toBeVisible(); + }); \ No newline at end of file diff --git a/enatega-multivendor-web/__tests__/tests/shop.spec.ts b/enatega-multivendor-web/__tests__/tests/shop.spec.ts new file mode 100644 index 00000000..43a46110 --- /dev/null +++ b/enatega-multivendor-web/__tests__/tests/shop.spec.ts @@ -0,0 +1,191 @@ +import { test, expect } from '@playwright/test'; + +test('check specific elements exist', async ({ page }) => { + await page.goto('http://localhost:3000/#/restaurant/pizza-shop'); + + const pepperoniDescriptionText = 'Loaded with Pepperoni and extra Cheese on a Pizza Sauce Base'; + + const spicyItalianText = 'Spicy Italian'; + const spicyItalianDescriptionText = 'Pepperoni, Double Italian Sausage, Red Chilli Flakes and a Pizza Sauce Base'; + + const papasFavouriteText = "Papa's Favourite"; + const papasFavouriteDescriptionText = 'Pepperoni, Italian Sausage, Mozzarella Cheeses, Italian Seasoning on a Pizza Sau...'; + + const americanHotText = 'American Hot'; + const americanHotDescriptionText = 'Loaded with Pepperoni, Jalapenos and Extra Cheese on a Pizza Sauce Base with Red...'; + + const superPapasText = "Super Papa's"; + const superPapasDescriptionText = 'Pepperoni, Italian Sausage, Mushrooms, Onions, Green Peppers, Black Olives on a ...'; + const signaturePizzasTitle = 'Signature Pizzas'; + const addButtonSelector = '.MuiButtonBase-root.MuiButton-root.MuiButton-text.MuiButton-textPrimary.MuiButton-sizeMedium.MuiButton-textSizeMedium.makeStyles-addContainer-504.css-1fvkalb-MuiButtonBase-root-MuiButton-root'; + + // Check for the Pepperoni item text + await expect(page.getByText('Pepperoni', { exact: true })).toBeVisible(); + await expect(page.getByText(pepperoniDescriptionText)).toBeVisible(); + await expect(page.locator('.MuiPaper-root > div > p').first()).toBeVisible(); + await expect(page.locator('.MuiTypography-root > .MuiTypography-root').first()).toBeVisible(); + + // Check for the Signature Pizzas title + await expect(page.locator('a').filter({ hasText: 'Signature Pizzas' })).toBeVisible(); + + // Check for the Spicy Italian item text + await expect(page.getByText(spicyItalianText)).toBeVisible(); + await expect(page.getByText(spicyItalianDescriptionText)).toBeVisible(); + + // Check for the Papa's Favourite item text + await expect(page.getByText(papasFavouriteText)).toBeVisible(); + await expect(page.getByText(papasFavouriteDescriptionText)).toBeVisible(); + + // Check for the American Hot item text + await expect(page.getByText('American Hot').first()).toBeVisible(); + await expect(page.getByText(americanHotDescriptionText)).toBeVisible(); + await expect(page.getByText('$ 5.60$').first()).toBeVisible(); + await expect(page.getByText('$ 10.60').first()).toBeVisible(); + + // Check for the Super Papa's item text + await expect(page.getByText(superPapasText)).toBeVisible(); + await expect(page.getByText(superPapasDescriptionText)).toBeVisible(); + + // Check for the Add buttons +// await expect(page.locator(addButtonSelector).nth(0)).toBeVisible(); +// await expect(page.locator(addButtonSelector).nth(1)).toBeVisible(); +// await expect(page.locator(addButtonSelector).nth(2)).toBeVisible(); +// await expect(page.locator(addButtonSelector).nth(3)).toBeVisible(); +// await expect(page.locator(addButtonSelector).nth(4)).toBeVisible(); + }); + + test('Check existence of elements2', async ({ page }) => { + await page.goto('http://localhost:3000/#/restaurant/pizza-shop'); + + // Check for Fajita Chicken + await expect(page.locator('text=Fajita Chicken')).toBeVisible(); + await expect(page.locator('text=Spicy Chicken,Jalapenos, Onions, Corn, Green Pepper, Ranch Drizzle on a Peri Per...')).toBeVisible(); + await expect(page.getByText('$ 5.60$').nth(1)).toBeVisible(); + await expect(page.getByText('$ 10.60').nth(1)).toBeVisible(); + + // Check for Peri Peri Chicken + await expect(page.getByText('Peri Peri Chicken', { exact: true })).toBeVisible(); + await expect(page.locator('text=Peri Peri Chicken, Onion, Green Pepper, Jalapenos , PERI PERI Sauce Drizzle on a...')).toBeVisible(); + await expect(page.locator('div:nth-child(2) > .MuiPaper-root > div > p').first()).toBeVisible(); + await expect(page.locator('div:nth-child(2) > .MuiPaper-root > div > p > .MuiTypography-root').first()).toBeVisible(); + + // Check for Garden Special + await expect(page.getByText('Tandoori Chicken', { exact: true })).toBeVisible(); + await expect(page.locator('text=Green Peppers, Mushrooms, Onions, Tomatoes, Black Olives, Italian Seasoning on a...')).toBeVisible(); + await expect(page.locator('div:nth-child(3) > .MuiPaper-root > div > p').first()).toBeVisible(); + await expect(page.locator('div:nth-child(3) > .MuiPaper-root > div > p > .MuiTypography-root').first()).toBeVisible(); + + // Check for Tandoori Chicken + await expect(page.getByText('Tandoori Chicken', { exact: true })).toBeVisible(); + await expect(page.locator('text=Spicy Chicken, Green Chilli, Green Peppers, Onions on a Tandoori Pizza Sauce Bas...')).toBeVisible(); + await expect(page.locator('div:nth-child(4) > .MuiPaper-root > div > p').first()).toBeVisible(); + await expect(page.locator('div:nth-child(4) > .MuiPaper-root > div > p > .MuiTypography-root').first()).toBeVisible(); + + + // Check for Chicken BBQ Ranch + await expect(page.locator('text=Chicken BBQ Ranch')).toBeVisible(); + await expect(page.locator('text=Grilled Chicken, Mushrooms, Onions, BBQ Sauce Drizzle on a Ranch Sauce Base')).toBeVisible(); + await expect(page.locator('text=$ 770.00')).toBeVisible(); + await expect(page.locator('text=$ 1470.00')).toBeVisible(); + + // Check for Spicy Buffalo Ranch + await expect(page.locator('text=Spicy Buffalo Ranch')).toBeVisible(); + await expect(page.locator('text=Sliced Chicken Poppers, Green Peppers, Mushrooms, Onions, Tomatoes, Ranch Drizzl...')).toBeVisible(); + await expect(page.locator('div:nth-child(5) > .MuiPaper-root > div > p').first()).toBeVisible(); + await expect(page.locator('div:nth-child(5) > .MuiPaper-root > div > p > .MuiTypography-root').first()).toBeVisible(); + + // Check for Hot Popper Passion + await expect(page.locator('text=Hot Popper Passion')).toBeVisible(); + await expect(page.locator('text=Tandoori Chicken Popper, Jalapeno, Green Pepper, Onion, Buffalo Sauce Drizzle wi...')).toBeVisible(); + await expect(page.locator('div:nth-child(6) > .MuiPaper-root > div > p').first()).toBeVisible(); + await expect(page.locator('div:nth-child(6) > .MuiPaper-root > div > p > .MuiTypography-root').first()).toBeVisible(); + + + // Check for Cheese + await expect(page.getByText('Cheese', { exact: true })).toBeVisible(); + await expect(page.locator('text=Cheese and Pizza Sauce Base')).toBeVisible(); + await expect(page.locator('div:nth-child(7) > .MuiPaper-root > div > p').first()).toBeVisible(); + await expect(page.locator('div:nth-child(7) > .MuiPaper-root > div > p > .MuiTypography-root').first()).toBeVisible(); + + // Check for American Hot + await expect(page.getByText('American Hot').first()).toBeVisible(); + await expect(page.getByText('Pepperoni, Jalapenos and Extra Cheese on a Pizza Sauce Base', { exact: true })).toBeVisible(); + await expect(page.locator('text=$ 5.00')).toBeVisible(); + }); + + + +test('check for italian food east', async ({ page }) => { + await page.goto('http://localhost:3000/#/restaurant/italian-food-east'); + + // Check each pizza item + await expect(page.locator('text=Cheese Pizza')).toBeVisible(); + await expect(page.getByText('Veggie Pizza', { exact: true })).toBeVisible(); + await expect(page.locator('text=Pepperoni Pizza')).toBeVisible(); + await expect(page.locator('text=Margherita Pizza')).toBeVisible(); + await expect(page.locator('text=BBQ Chicken Pizza')).toBeVisible(); + await expect(page.locator('text=Hawaiian Pizza')).toBeVisible(); + await expect(page.locator('text=Buffalo Pizza')).toBeVisible(); + await expect(page.locator('text=Pizza Capricciosa')).toBeVisible(); + await expect(page.locator('text=Kale And Sausage Pizza')).toBeVisible(); + await expect(page.locator('text=Deluxe Veggie Pizza')).toBeVisible(); + await expect(page.locator('text=Mexican Green Wave Pizza')).toBeVisible(); +}); + + +test('check if specific pasta items exist', async ({ page }) => { + await page.goto('http://localhost:3000/#/restaurant/italian-food-east'); + + // Check each pasta item + await expect(page.locator('p.MuiTypography-body1:has-text("Spaghetti")')).toBeVisible(); + await expect(page.locator('p.MuiTypography-body1:has-text("Ravioli")')).toBeVisible(); + await expect(page.locator('p.MuiTypography-body1:has-text("Penne Pasta")')).toBeVisible(); + await expect(page.locator('p.MuiTypography-body1:has-text("Macaroni")')).toBeVisible(); + await expect(page.locator('p.MuiTypography-body1:has-text("Lasagna")')).toBeVisible(); + await expect(page.locator('p.MuiTypography-body1:has-text("Gnocchi")')).toBeVisible(); + await expect(page.locator('p.MuiTypography-body1:has-text("Linguine")')).toBeVisible(); + await expect(page.locator('p.MuiTypography-body1:has-text("Vermicelli")')).toBeVisible(); +}); + + +test('Verify elements and interact with them', async ({ page }) => { + await page.goto('http://localhost:3000/#/restaurant/italian-food-east'); + + // Verify the "Add" button for Coke is present and click it + const cokeAddButton = page.locator('.makeStyles-imageContainer-6 > .MuiButtonBase-root'); + await expect(cokeAddButton).toBeVisible(); + await cokeAddButton.click(); + + // Verify the price of Sprite and assert it is correct + const spritePrice = page.getByText('$ 5.00', { exact: true }); + await expect(spritePrice).toBeVisible(); + +}); + +test('check presence of footer elements', async ({ page }) => { + await page.goto('http://localhost:3000/#/restaurant/italian-food-east'); + + // Check for the presence of "Enatega" header and description + const header = page.locator('h4:has-text("Enatega")'); + const description = page.locator('p:has-text("Enatega is an open-source delivery management platform for the future.")'); + + await expect(header).toBeVisible(); + await expect(description).toBeVisible(); + + // Check for the presence of "Links" section + await expect(page.locator('p:has-text("Links")')).toBeVisible(); + await expect(page.locator('a:has-text("Home")')).toBeVisible(); + await expect(page.locator('a:has-text("Privacy Policy")')).toBeVisible(); + await expect(page.locator('a:has-text("Terms and Conditions")')).toBeVisible(); + + // Check for the presence of social media icons in the "Follow Us" section + await expect(page.locator('svg[data-testid="FacebookIcon"]')).toBeVisible(); + await expect(page.locator('svg[data-testid="TwitterIcon"]')).toBeVisible(); + await expect(page.locator('svg[data-testid="InstagramIcon"]')).toBeVisible(); + await expect(page.locator('svg[data-testid="LinkedInIcon"]')).toBeVisible(); + await expect(page.locator('svg[data-testid="GitHubIcon"]')).toBeVisible(); + + // Check for the presence of "Powered By" section with "ninjascode" + await expect(page.locator('p:has-text("Powered By")')).toBeVisible(); + await expect(page.locator('p:has-text("ninjascode")')).toBeVisible(); +}); diff --git a/enatega-multivendor-web/__tests__/tests/terms.spec.ts b/enatega-multivendor-web/__tests__/tests/terms.spec.ts new file mode 100644 index 00000000..2504b7d8 --- /dev/null +++ b/enatega-multivendor-web/__tests__/tests/terms.spec.ts @@ -0,0 +1,353 @@ +import { test, expect } from '@playwright/test'; + + +test('check if specific elements exist', async ({ page }) => { + await page.goto('http://localhost:3000/#/terms'); + + const divSelector = 'div:has-text("TERMS AND CONDITIONS | ENATEGA")'; + + // Check if the
element contains the correct text + const divElement = await page.getByText('TERMS AND CONDITIONS | ENATEGAPublished: 2021TERMS OF USEThese Terms of Use (“'); + await expect(divElement).toBeVisible(); + + // Verify that the

within the
contains the correct text + const h4Text = 'TERMS AND CONDITIONS | ENATEGA'; + const h4Element = divElement.locator(`h4:has-text("${h4Text}")`); + await expect(h4Element).toHaveText(h4Text); + }); + + +test('check if the text exists in h6 elements within a list item', async ({ page }) => { + await page.goto('http://localhost:3000/#/terms'); + + // Check for the presence of the text "TERMS OF USE" + const termsOfUse = await page.getByRole('heading', { name: 'TERMS OF USE', exact: true }); + await expect(termsOfUse).toBeVisible(); + + const termsText = await page.locator('text=These Terms of Use (“Terms”) govern your use of the websites and mobile applications provided by enatega'); + await expect(termsText).toBeVisible(); + + const platformsText = await page.locator('text=The Platforms may be used by (i) natural persons who have reached 18 years of age and (ii) corporate legal entities, e.g companies.'); + await expect(platformsText).toBeVisible(); + + const usersText = await page.locator('text=Users below the age of 18 must obtain consent from parent(s) or legal guardian(s), who by accepting these Terms shall agree to take responsibility for your actions and any charges associated with your use of the Platforms and/or purchase of Goods.'); + await expect(usersText).toBeVisible(); + + const changeTermsText = await page.locator('text=Enatega reserves the right to change or modify these Terms (including our policies which are incorporated into these Terms) at any time.'); + await expect(changeTermsText).toBeVisible(); + + const enategaText = await page.getByRole('link', { name: 'Enatega' }); + await expect(enategaText).toBeVisible(); + + // Check for the presence of the text "What we do" + const whatWeDoText = await page.locator('text=What we do'); + await expect(whatWeDoText).toBeVisible(); + + const platformsText2 = await page.locator('text=Through our Platforms, enatega links you to the vendors (“Vendors”) for you to order a variety of goods including prepared meals, non-prepared food and miscellaneous non-food items (hereinafter collectively referred to as "Goods") to be delivered to you. When you place an order for Goods from our Vendors (“Order”), enatega acts as an agent on behalf of that Vendor to facilitate, process and conclude the order and subsequently for either us or the Vendor to deliver your Order to you. Vendors may be owned and operated by third party vendors, our affiliate companies, or us.'); + await expect(platformsText2).toBeVisible(); + + // Check for the presence of the text "How to contact us" + const contactUsText = await page.locator('text=How to contact us'); + await expect(contactUsText).toBeVisible(); + + const supportText = await page.locator('text=For customer support, you may reach out to us via email or through our in-app customer support chat feature.'); + await expect(supportText).toBeVisible(); + + const useOfPlatformsText = await page.locator('text=Use of the Platforms and enatega Account'); + await expect(useOfPlatformsText).toBeVisible(); + + const registerText = await page.locator('text=You will need to register for a enatega account for you to use the Platform. When you register for a enatega account we will ask you to provide your personal information including a valid email address, a mobile phone number and a unique password. To purchase an Order, depending on which payment method you opt for, you may need to provide us with your credit card details. Your unique password should not be shared with anyone and you agree to keep it secret at all times. You are solely responsible for keeping your password safe. Save for cases of fraud or abuse which are not your fault, you accept that all Orders placed under your enatega account are your sole responsibility.'); + await expect(registerText).toBeVisible(); + + const liabilityText = await page.locator('text=enatega shall not be liable for Orders that encounter delivery issues due to incomplete, incorrect or missing information provided by you. You are obliged to provide information that is complete, accurate and truthful for the proper processing of the Order, including your delivery address and contact information.'); + await expect(liabilityText).toBeVisible(); + + const deleteAccountText = await page.locator('text=If you wish to delete your enatega account, please send us an email requesting the same. We may restrict, suspend or terminate your enatega account and/or use of the Platforms, if we reasonably believe that:'); + await expect(deleteAccountText).toBeVisible(); + + const someoneElseUsingAccountText = await page.locator('text=someone other than you is using your enatega account; or'); + await expect(someoneElseUsingAccountText).toBeVisible(); + + const breachText = await page.locator('text=where you are suspected or discovered to have been involved in any activity or conduct that is in breach of these Terms, our policies and guidelines, or involved in activity or conduct which we deem in our sole discretion to be an abuse of the Platforms.'); + await expect(breachText).toBeVisible(); + + const intellectualPropertyHeaderText = await page.getByRole('heading', { name: 'Intellectual Property', exact: true }) + await expect(intellectualPropertyHeaderText).toBeVisible(); + + const intellectualPropertyText1 = await page.locator('text=All trademarks, logos, images, and service marks, including these Terms as displayed on the Platforms or in our marketing material, whether registered or unregistered, are the intellectual property of enatega and/or third parties who have authorised us with the use (collectively the “Trademarks”).'); + await expect(intellectualPropertyText1).toBeVisible(); + + const intellectualPropertyText2 = await page.locator('text=You may not use, copy, reproduce, republish, upload, post, transmit, distribute, or modify these Trademarks in any way without our prior express written consent.'); + await expect(intellectualPropertyText2).toBeVisible(); + + const intellectualPropertyText3 = await page.locator('text=The use of enatega\'s trademarks on any other website not approved by us is strictly prohibited.'); + await expect(intellectualPropertyText3).toBeVisible(); + + const intellectualPropertyText4 = await page.locator('text=Enatega will aggressively enforce its intellectual property rights to the fullest extent of the law, including criminal prosecution.'); + await expect(intellectualPropertyText4).toBeVisible(); + + const intellectualPropertyText5 = await page.locator('text=Enatega neither warrants nor represents that your use of materials displayed on the Platforms will not infringe rights of third parties not owned by or affiliated with enatega.'); + await expect(intellectualPropertyText5).toBeVisible(); + + const intellectualPropertyText6 = await page.locator('text=Use of any materials on the Platforms is at your own risk.'); + await expect(intellectualPropertyText6).toBeVisible(); + }); + + test('Check if specific elements exist on the page', async ({ page }) => { + await page.goto('http://localhost:3000/#/terms'); + + const termsOfUse = await page.getByRole('heading', { name: 'Orders', exact: true }); + await expect(termsOfUse).toBeVisible(); + + const whenYouPlaceAnOrder = await page.locator('text=When you place an Order with enatega'); + await expect(whenYouPlaceAnOrder).toBeVisible(); + + const minimumOrderValue = await page.locator('text=Minimum Order Value'); + await expect(minimumOrderValue).toBeVisible(); + + const specialInstructions = await page.locator('text=Special Instructions'); + await expect(specialInstructions).toBeVisible(); + + const allergens = await page.locator('text=Allergens'); + await expect(allergens).toBeVisible(); + + const priorToPlacingOrder = await page.locator('text=Prior to placing the Order'); + await expect(priorToPlacingOrder).toBeVisible(); + + const placingOrder = await page.getByRole('heading', { name: 'Placing the Order', exact: true }); + await expect(placingOrder).toBeVisible(); + + const cancellingOrder = await page.locator('text=Cancelling an Order'); + await expect(cancellingOrder).toBeVisible(); + + const refunds = await page.getByRole('heading', { name: 'Refunds', exact: true }); + await expect(refunds).toBeVisible(); + + const onlinePaymentOrders = await page.locator('text=Online Payment Orders'); + await expect(onlinePaymentOrders).toBeVisible(); + + const cashOnDeliveryOrders = await page.locator('text=Cash-on-Delivery Orders'); + await expect(cashOnDeliveryOrders).toBeVisible(); + + const reservesRightToCancel = await page.locator('text=reserves the right to cancel any Order'); + await expect(reservesRightToCancel).toBeVisible(); + }); + + test('Check if specific elements exist on the Prices and Payments section', async ({ page }) => { + await page.goto('http://localhost:3000/#/terms'); + + const pricesAndPaymentsHeading = await page.locator('text=Prices and Payments'); + await expect(pricesAndPaymentsHeading).toBeVisible(); + + const pricesQuoted = await page.locator('text=Prices quoted on the Platform shall be displayed in the applicable country’s national currency'); + await expect(pricesQuoted).toBeVisible(); + + const includeTax = await page.locator('text=include TAX, VAT or such other equivalent tax'); + await expect(includeTax).toBeVisible(); + + const excludeTax = await page.locator('text=exclude TAX, VAT or such other equivalent tax'); + await expect(excludeTax).toBeVisible(); + + const breakdownOfPrices = await page.locator('text=A breakdown of the prices and additional charges are displayed before Checkout'); + await expect(breakdownOfPrices).toBeVisible(); + + const deliveryFeesChargeable = await page.locator('text=Delivery fees are chargeable on every Order unless'); + await expect(deliveryFeesChargeable).toBeVisible(); + + const optForPickUp = await page.locator('text=you opt to collect your Order directly from the Vendor'); + await expect(optForPickUp).toBeVisible(); + + const validPromotionalVoucher = await page.locator('text=you have a valid promotional or discount voucher and apply it at Checkout'); + await expect(validPromotionalVoucher).toBeVisible(); + + const pricesSubjectToChange = await page.locator('text=Prices indicated on the Platforms are as at the time of each Order and may be subject to change'); + await expect(pricesSubjectToChange).toBeVisible(); + + const paymentMethods = await page.locator('text=You can choose to pay for an Order using any of the different payment methods offered on the Platforms'); + await expect(paymentMethods).toBeVisible(); + + const paymentPartners = await page.locator('text=Our payment partners: Visa, Mastercard, American Express, Google Pay, PayPal, Apple Pay'); + await expect(paymentPartners).toBeVisible(); + + const cashOnDelivery = await page.getByRole('heading', { name: 'Cash-on-Delivery Orders' }); + await expect(cashOnDelivery).toBeVisible(); + + const suchOtherPaymentMethod = await page.locator('text=Such other payment method we offer from time to time'); + await expect(suchOtherPaymentMethod).toBeVisible(); + + const creditInAccount = await page.locator('text=If you have existing credit in your enatega account or valid promotional or discount vouchers'); + await expect(creditInAccount).toBeVisible(); + + const emailConfirmation = await page.locator('text=After an Order is successfully placed, you will receive an email confirmation from us with your Order receipt'); + await expect(emailConfirmation).toBeVisible(); + + const paymentMethodsHeading = await page.getByRole('heading', { name: 'Payment Methods', exact: true }); + await expect(paymentMethodsHeading).toBeVisible(); + + const enategaReservesRight = await page.locator('text=Enatega reserves the right to offer additional payment methods and/or remove existing payment methods'); + await expect(enategaReservesRight).toBeVisible(); + + const sufficientFunds = await page.locator('text=You must ensure that you have sufficient funds on your credit and debit card to fulfil payment of an Order'); + await expect(sufficientFunds).toBeVisible(); + }); + + +test('Check essential elements in Delivery, Pick-Up and Vendor Delivery section', async ({ page }) => { + await page.goto('http://localhost:3000/#/terms'); + + const deliveryHeading = await page.locator('text=Delivery, Pick-Up and Vendor Delivery'); + await expect(deliveryHeading).toBeVisible(); + + const deliveryAreas = await page.getByRole('heading', { name: 'Delivery Areas', exact: true }); + await expect(deliveryAreas).toBeVisible(); + + const deliveryTime = await page.getByRole('heading', { name: 'Delivery Time', exact: true }); + await expect(deliveryTime).toBeVisible(); + + const unsuccessfulDeliveries = await page.locator('text=Unsuccessful or Failed Deliveries'); + await expect(unsuccessfulDeliveries).toBeVisible(); + + const noShowCancellations = await page.locator('text=No-show Cancellations'); + await expect(noShowCancellations).toBeVisible(); + + const wrongOrder = await page.locator('text=Wrong Order, Missing Items, Defective Goods'); + await expect(wrongOrder).toBeVisible(); + + const orderPickUp = await page.locator('text=Order Pick-Up'); + await expect(orderPickUp).toBeVisible(); + + const vendorDelivery = await page.getByRole('heading', { name: 'Vendor Delivery', exact: true }); + await expect(vendorDelivery).toBeVisible(); + }); + + test('Check essential elements in Vouchers, Discounts and Promotions section', async ({ page }) => { + await page.goto('http://localhost:3000/#/terms'); + + const vouchersHeading = await page.locator('text=Vouchers, Discounts and Promotions'); + await expect(vouchersHeading).toBeVisible(); + + const vouchersPromo = await page.locator('text=marketing and promotional campaigns which offer voucher codes, discounts, and other promotional offers'); + await expect(vouchersPromo).toBeVisible(); + + const platformUse = await page.locator('text=Vouchers can only be used on our Platforms'); + await expect(platformUse).toBeVisible(); + + const noCashExchange = await page.locator('text=Vouchers cannot be exchanged for cash'); + await expect(noCashExchange).toBeVisible(); + + const voidVoucher = await page.locator('text=void, discontinue or reject the use of any Voucher without prior notice'); + await expect(voidVoucher).toBeVisible(); + + const excludeVendors = await page.locator('text=exclude certain Vendors from the use of Vouchers at any time without prior notice'); + await expect(excludeVendors).toBeVisible(); + }); + + +test('should check for specific text in the document', async ({ page }) => { + await page.goto('http://localhost:3000/#/terms'); + + const mainHeading = await page.locator('text=Representations, Warranties and Limitation of Liabilities'); + await expect(mainHeading).toBeVisible(); + + // Check for sub-heading texts + const representationsHeading = await page.locator('text=Representations and Warranties'); + await expect(representationsHeading).toBeVisible(); + + const limitationHeading = await page.getByRole('heading', { name: 'Limitation of Liability', exact: true }); + await expect(limitationHeading).toBeVisible(); + + const vendorsHeading = await page.locator('text=Vendor’s representations'); + await expect(vendorsHeading).toBeVisible(); + + // Check for shorter snippets of text + const representationsTextSnippet = await page.locator('text=your use of or reliance upon the Platforms and any content'); + await expect(representationsTextSnippet).toBeVisible(); + + const limitationTextSnippet = await page.locator('text=the enatega entities, their agents, representatives, and service providers'); + await expect(limitationTextSnippet).toBeVisible(); + + const vendorsTextSnippet = await page.locator('text=actions or omissions of the Vendor'); + await expect(vendorsTextSnippet).toBeVisible(); + + const vendorLiabilityHeading = await page.locator('text=Vendor Liability'); + await expect(vendorLiabilityHeading).toBeVisible(); + + const vendorLiabilityText = await page.locator('text=Vendors are responsible for the preparation, condition and quality of Goods. In cases of Vendor Delivery, Vendors are responsible for delivery of the Goods and/or Orders. Enatega shall not be liable for any loss or damage arising from your contractual relationship with the Vendor.'); + await expect(vendorLiabilityText).toBeVisible(); + + const personalDataHeading = await page.locator('text=Personal Data (Personal Information) Protection'); + await expect(personalDataHeading).toBeVisible(); + + const personalDataText = await page.locator('text=You agree and consent to enatega and any of its affiliate companies collecting, using, processing and disclosing your Personal Data in accordance with these Terms and as further described in our Privacy Policy. Our Privacy Policy is available via the links on our Platforms, and shall form a part of these Terms.'); + await expect(personalDataText).toBeVisible(); + + const indemnityHeading = await page.locator('text=Indemnity'); + await expect(indemnityHeading).toBeVisible(); + + const indemnityText = await page.locator('text=You agree to indemnify, defend, hold harmless enatega, its directors, officers, employees, representatives, agents, and affiliates, from any and all third party claims, liability, damages and/or costs (including but not limited to, legal fees) arising from your use of the Platforms or your breach of these Terms.'); + await expect(indemnityText).toBeVisible(); + + const ThirdParty = await page.getByRole('heading', { name: 'Third Party Links and Websites', exact: true }); + await expect(ThirdParty).toBeVisible(); + + const Termination = await page.getByRole('heading', { name: 'Termination', exact: true }); + await expect(Termination).toBeVisible(); + + const Amendments = await page.getByRole('heading', { name: 'Amendments', exact: true }); + await expect(Amendments).toBeVisible(); + + const Severability = await page.getByRole('heading', { name: 'Severability', exact: true }); + await expect(Severability).toBeVisible(); + + const GoverningLaw = await page.getByRole('heading', { name: 'Governing Law', exact: true }); + await expect(GoverningLaw).toBeVisible(); + + const ContactUs = await page.getByRole('heading', { name: 'Contact Us', exact: true }); + await expect(ContactUs).toBeVisible(); + + const PrevailingLanguage = await page.getByRole('heading', { name: 'Prevailing Language', exact: true }); + await expect(PrevailingLanguage).toBeVisible(); +}); + +test('should check for footer element existence', async ({ page }) => { + await page.goto('http://localhost:3000/#/terms'); + + // Locate the footer element + const footer = await page.locator('div.MuiGrid-root.MuiGrid-container.css-1vam7s3-MuiGrid-root'); + + // Check if the footer element is visible + await expect(footer).toBeVisible(); + + // Optional: Check for specific text within the footer + await expect(footer.locator('h4').filter({ hasText: /^Enatega$/ })).toBeVisible(); + await expect(footer.locator('text=Enatega is an open-source delivery management platform for the future. We prioritize innovation, flexibility, and affordability, and offer a scalable, customizable solution that streamlines your delivery processes.')).toBeVisible(); + await expect(footer.locator('text=Links')).toBeVisible(); + await expect(footer.locator('text=Home')).toBeVisible(); + await expect(footer.locator('text=Privacy Policy')).toBeVisible(); + await expect(footer.locator('text=Terms and Conditions')).toBeVisible(); + await expect(footer.getByText('Enatega – © 2022 All Rights').first()).toBeVisible(); + await expect(footer.locator('text=Follow Us')).toBeVisible(); +}); + +test('should redirect to the Home page when clicking the Home link', async ({ page }) => { + await page.goto('http://localhost:3000/#/terms'); + + await Promise.all([ + page.waitForNavigation(), // Wait for the navigation + page.click('a[href="#/"]') + ]); + + await expect(page).toHaveURL('http://localhost:3000/#/'); +}); + +test('should redirect to the Privacy Policy page when clicking the Privacy Policy link', async ({ page }) => { + await page.goto('http://localhost:3000/#/terms'); + + await Promise.all([ + page.waitForNavigation(), // Wait for the navigation + page.click('a[href="#/privacy"]') + ]); + + // Verify the URL after the redirection + await expect(page).toHaveURL('http://localhost:3000/#/privacy'); +}); \ No newline at end of file diff --git a/enatega-multivendor-web/package-lock.json b/enatega-multivendor-web/package-lock.json index 35147128..8fd19dba 100644 --- a/enatega-multivendor-web/package-lock.json +++ b/enatega-multivendor-web/package-lock.json @@ -19,7 +19,6 @@ "@react-google-maps/api": "^2.0.2", "@sentry/react": "^6.16.1", "@sentry/tracing": "^6.16.1", - "@tawk.to/tawk-messenger-react": "^1.0.0", "@types/react": "^17.0.24", "amplitude-js": "^8.14.1", "autosuggest-highlight": "^3.1.1", @@ -4440,129 +4439,6 @@ "url": "https://github.com/sponsors/gregberge" } }, - "node_modules/@tawk.to/tawk-messenger-react": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@tawk.to/tawk-messenger-react/-/tawk-messenger-react-1.0.0.tgz", - "integrity": "sha512-4jGmgz47bcbEIdX3Qvi8Uj7//vroIBX1AKFpnTg9urgCejPShprlGPrGkSj0uWjfNTMD6S0Sh+JP0CkopSyXDw==", - "dependencies": { - "ansi-styles": "^4.3.0", - "aria-query": "^5.0.0", - "babel-plugin-macros": "^3.1.0", - "babel-plugin-named-asset-import": "^0.3.8", - "babel-preset-react-app": "^10.0.1", - "bindings": "^1.5.0", - "body-parser": "^1.19.1", - "browserslist": "^4.19.1", - "camelcase": "^6.3.0", - "caniuse-lite": "^1.0.30001296", - "chalk": "^4.1.2", - "color-convert": "^2.0.1", - "color-name": "^1.1.4", - "color-string": "^1.9.0", - "confusing-browser-globals": "^1.0.11", - "content-disposition": "^0.5.4", - "cookie": "^0.4.1", - "core-js": "^3.20.2", - "core-js-compat": "^3.20.2", - "core-js-pure": "^3.20.2", - "cross-spawn": "^7.0.3", - "css": "^3.0.0", - "css-select": "^4.2.1", - "css-what": "^5.1.0", - "debug": "^4.3.3", - "diff-sequences": "^27.4.0", - "dom-serializer": "^1.3.2", - "domelementtype": "^2.2.0", - "domhandler": "^4.3.0", - "domutils": "^2.8.0", - "electron-to-chromium": "^1.4.35", - "emoji-regex": "^9.2.2", - "escape-string-regexp": "^4.0.0", - "eslint-module-utils": "^2.7.2", - "eslint-plugin-import": "^2.25.4", - "estraverse": "^5.3.0", - "execa": "^4.1.0", - "express": "^4.17.2", - "file-uri-to-path": "^1.0.0", - "follow-redirects": "^1.14.6", - "get-stream": "^5.2.0", - "graceful-fs": "^4.2.9", - "has-flag": "^4.0.0", - "http-errors": "^1.8.1", - "http-parser-js": "^0.5.5", - "ignore": "^5.2.0", - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "is-descriptor": "^1.0.2", - "is-extendable": "^1.0.1", - "is-negative-zero": "^2.0.2", - "is-obj": "^1.0.1", - "is-stream": "^2.0.1", - "is-weakref": "^1.0.2", - "istanbul-reports": "^3.1.3", - "jest-cli": "^26.6.3", - "jest-diff": "^27.4.6", - "jest-get-type": "^27.4.0", - "jest-worker": "^27.4.6", - "minipass": "^3.1.6", - "nan": "^2.15.0", - "npm-run-path": "^4.0.1", - "nth-check": "^2.0.1", - "object-inspect": "^1.12.0", - "path-key": "^3.1.1", - "picomatch": "^2.3.1", - "pirates": "^4.0.4", - "postcss-selector-parser": "^6.0.8", - "postcss-value-parser": "^4.2.0", - "pretty-format": "^27.4.6", - "prop-types": "^15.8.1", - "qs": "^6.9.6", - "raw-body": "^2.4.2", - "react": "^17.0.2", - "react-error-overlay": "^6.0.10", - "react-is": "^17.0.2", - "resolve-from": "^5.0.0", - "send": "^0.17.2", - "serve-static": "^1.14.2", - "setprototypeof": "^1.2.0", - "shebang-command": "^2.0.0", - "shebang-regex": "^3.0.0", - "sockjs": "^0.3.24", - "source-map": "^0.6.1", - "source-map-js": "^1.0.1", - "source-map-resolve": "^0.6.0", - "string_decoder": "^1.3.0", - "strip-bom": "^4.0.0", - "supports-color": "^7.2.0", - "supports-preserve-symlinks-flag": "^1.0.0", - "table": "^6.7.5", - "toidentifier": "^1.0.1", - "tslib": "^2.3.1", - "type-fest": "^0.21.3", - "typescript": "^4.5.4", - "url-parse": "^1.5.4", - "util.promisify": "^1.0.0", - "which": "^2.0.2" - }, - "peerDependencies": { - "react": "^17.0.2", - "react-dom": "^17.0.2", - "react-scripts": "4.0.3" - } - }, - "node_modules/@tawk.to/tawk-messenger-react/node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" - }, - "node_modules/@tawk.to/tawk-messenger-react/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/@tootallnate/once": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", @@ -6318,6 +6194,7 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "optional": true, "dependencies": { "file-uri-to-path": "1.0.0" } @@ -6337,71 +6214,6 @@ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" }, - "node_modules/body-parser": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", - "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/body-parser/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/body-parser/node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/body-parser/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/body-parser/node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/bonjour": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", @@ -7333,14 +7145,6 @@ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" }, - "node_modules/cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", @@ -7400,16 +7204,6 @@ "url": "https://opencollective.com/core-js" } }, - "node_modules/core-js-pure": { - "version": "3.36.0", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.36.0.tgz", - "integrity": "sha512-cN28qmhRNgbMZZMc/RFu5w8pK9VJzpb2rJVR/lHuZJKwmXnoWOpXmMkxqBB514igkp1Hu8WGROsiOAzUcKdHOQ==", - "hasInstallScript": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", @@ -7511,16 +7305,6 @@ "node": ">=4" } }, - "node_modules/css": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/css/-/css-3.0.0.tgz", - "integrity": "sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==", - "dependencies": { - "inherits": "^2.0.4", - "source-map": "^0.6.1", - "source-map-resolve": "^0.6.0" - } - }, "node_modules/css-blank-pseudo": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-0.1.4.tgz", @@ -7727,25 +7511,6 @@ "is-in-browser": "^1.0.2" } }, - "node_modules/css-what": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz", - "integrity": "sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw==", - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/cssdb": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-4.4.0.tgz", @@ -8398,14 +8163,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, - "node_modules/diff-sequences": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", - "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, "node_modules/diffie-hellman": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", @@ -10250,7 +10007,8 @@ "node_modules/file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "optional": true }, "node_modules/filelist": { "version": "1.0.4", @@ -11544,37 +11302,6 @@ "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==" }, - "node_modules/http-errors": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", - "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", - "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/http-errors/node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/http-errors/node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/http-parser-js": { "version": "0.5.8", "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", @@ -12970,20 +12697,6 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" }, - "node_modules/jest-diff": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", - "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, "node_modules/jest-docblock": { "version": "26.0.0", "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-26.0.0.tgz", @@ -13070,14 +12783,6 @@ "node": ">= 10.14.2" } }, - "node_modules/jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, "node_modules/jest-haste-map": { "version": "26.6.2", "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.6.2.tgz", @@ -14711,7 +14416,8 @@ "node_modules/nan": { "version": "2.18.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.18.0.tgz", - "integrity": "sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w==" + "integrity": "sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w==", + "optional": true }, "node_modules/nanoid": { "version": "3.3.7", @@ -17033,35 +16739,6 @@ "renderkid": "^2.0.4" } }, - "node_modules/pretty-format": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", - "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", - "dependencies": { - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^17.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/pretty-format/node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" - }, "node_modules/process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", @@ -17337,35 +17014,6 @@ "node": ">= 0.6" } }, - "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/raw-body/node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/react": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", @@ -18051,6 +17699,20 @@ "node": ">=10" } }, + "node_modules/react-scripts/node_modules/type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "license": "(MIT OR CC0-1.0)", + "optional": true, + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/react-scroll": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/react-scroll/-/react-scroll-1.9.0.tgz", @@ -19446,79 +19108,6 @@ "semver": "bin/semver.js" } }, - "node_modules/send": { - "version": "0.17.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz", - "integrity": "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==", - "dependencies": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "1.8.1", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "~2.3.0", - "range-parser": "~1.2.1", - "statuses": "~1.5.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/send/node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/send/node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/send/node_modules/destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha512-3NdhDuEXnfun/z7x9GOElY49LoqVHoGScmOKwmxhsS8N5Y+Z8KyPPDnaSzqWgYt/ji4mqwfTS34Htrk0zPIXVg==" - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/send/node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/send/node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/serialize-javascript": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", @@ -20107,24 +19696,6 @@ "node": ">=0.10.0" } }, - "node_modules/source-map-resolve": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.6.0.tgz", - "integrity": "sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==", - "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", - "dependencies": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0" - } - }, - "node_modules/source-map-resolve/node_modules/decode-uri-component": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", - "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", - "engines": { - "node": ">=0.10" - } - }, "node_modules/source-map-support": { "version": "0.5.21", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", @@ -21655,6 +21226,7 @@ "version": "4.9.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -21996,23 +21568,6 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, - "node_modules/util.promisify": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.1.2.tgz", - "integrity": "sha512-PBdZ03m1kBnQ5cjjO0ZvJMJS+QsbyIcFwi4hY4U76OQsCO9JrOYjbCFgIF76ccFg9xnJo7ZHPkqyj1GqmdS7MA==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "object.getownpropertydescriptors": "^2.1.6", - "safe-array-concat": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/util/node_modules/inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",