diff --git a/.env.example b/.env.example index c89add4..885736b 100644 --- a/.env.example +++ b/.env.example @@ -1,18 +1,25 @@ +# Supabase Configuration PROJECT_ID=supabase-project-id NEXT_PUBLIC_SUPABASE_URL=supabase-project-url -NEXT_PUBLIC_SUPABASE_URL_SOURCE = supabase-project-url-without-https:// (ex: https://example.com -> example.com) +NEXT_PUBLIC_SUPABASE_URL_SOURCE=supabase-project-url-without-https (e.g., https://example.com -> example.com) NEXT_PUBLIC_SUPABASE_ANON_KEY=supabase-anon-key -NEXT_PUBLIC_AUTH_GOOGLE_ID=google-auth-token -NEXT_PUBLIC_AUTH_GOOGLE_SECRET=google-secret-key -NEXT_PUBLIC_TEST_URL=url-to-run-testing-server +SUPABASE_SERVICE_ROLE_KEY=supabase-service-role-key + +# Google Authentication Configuration +NEXT_PUBLIC_AUTH_GOOGLE_ID=google-auth-client-id +NEXT_PUBLIC_AUTH_GOOGLE_SECRET=google-auth-client-secret + +# Stripe Configuration NEXT_PUBLIC_STRIPE_PUBLIC_KEY=stripe-public-key STRIPE_SECRET_KEY=stripe-secret-key -SUPABASE_SERVICE_ROLE_KEY=superbase-service-role-key -# Admin User Credentials -NEXT_PUBLIC_ADMIN_EMAIL=supabase-dummy-admin-email-for-testing -NEXT_PUBLIC_ADMIN_PASSWORD=supabase-dummy-admin-password-for-testing +# Testing Server URL +NEXT_PUBLIC_TEST_URL=testing-server-url -# Regular User Credentials -NEXT_PUBLIC_USER_EMAIL=supabase-dummy-user-email-for-testing -NEXT_PUBLIC_USER_PASSWORD=supabase-dummy-user-password-for-testing \ No newline at end of file +# Admin User Credentials (Must exist in the real database) +NEXT_PUBLIC_ADMIN_EMAIL=admin@example.com +NEXT_PUBLIC_ADMIN_PASSWORD=admin-secure-password + +# Temporary Regular User Credentials for Testing (Delete after test completion) +NEXT_PUBLIC_TEST_USER_EMAIL=test-user@example.com +NEXT_PUBLIC_TEST_USER_PASSWORD=test-user-password diff --git a/.env.local.example b/.env.local.example index c89add4..885736b 100644 --- a/.env.local.example +++ b/.env.local.example @@ -1,18 +1,25 @@ +# Supabase Configuration PROJECT_ID=supabase-project-id NEXT_PUBLIC_SUPABASE_URL=supabase-project-url -NEXT_PUBLIC_SUPABASE_URL_SOURCE = supabase-project-url-without-https:// (ex: https://example.com -> example.com) +NEXT_PUBLIC_SUPABASE_URL_SOURCE=supabase-project-url-without-https (e.g., https://example.com -> example.com) NEXT_PUBLIC_SUPABASE_ANON_KEY=supabase-anon-key -NEXT_PUBLIC_AUTH_GOOGLE_ID=google-auth-token -NEXT_PUBLIC_AUTH_GOOGLE_SECRET=google-secret-key -NEXT_PUBLIC_TEST_URL=url-to-run-testing-server +SUPABASE_SERVICE_ROLE_KEY=supabase-service-role-key + +# Google Authentication Configuration +NEXT_PUBLIC_AUTH_GOOGLE_ID=google-auth-client-id +NEXT_PUBLIC_AUTH_GOOGLE_SECRET=google-auth-client-secret + +# Stripe Configuration NEXT_PUBLIC_STRIPE_PUBLIC_KEY=stripe-public-key STRIPE_SECRET_KEY=stripe-secret-key -SUPABASE_SERVICE_ROLE_KEY=superbase-service-role-key -# Admin User Credentials -NEXT_PUBLIC_ADMIN_EMAIL=supabase-dummy-admin-email-for-testing -NEXT_PUBLIC_ADMIN_PASSWORD=supabase-dummy-admin-password-for-testing +# Testing Server URL +NEXT_PUBLIC_TEST_URL=testing-server-url -# Regular User Credentials -NEXT_PUBLIC_USER_EMAIL=supabase-dummy-user-email-for-testing -NEXT_PUBLIC_USER_PASSWORD=supabase-dummy-user-password-for-testing \ No newline at end of file +# Admin User Credentials (Must exist in the real database) +NEXT_PUBLIC_ADMIN_EMAIL=admin@example.com +NEXT_PUBLIC_ADMIN_PASSWORD=admin-secure-password + +# Temporary Regular User Credentials for Testing (Delete after test completion) +NEXT_PUBLIC_TEST_USER_EMAIL=test-user@example.com +NEXT_PUBLIC_TEST_USER_PASSWORD=test-user-password diff --git a/playwright.config.ts b/playwright.config.ts index 4854f23..1ffe725 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -13,6 +13,7 @@ dotenv.config({ path: path.resolve(__dirname, '.env') }); */ export default defineConfig({ globalSetup: require.resolve('./test_util/global-setup'), + globalTeardown: require.resolve('./test_util/global-teardown'), testDir: './tests', /* Run tests in files in parallel */ fullyParallel: true, @@ -27,7 +28,7 @@ export default defineConfig({ /* 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', + baseURL: process.env.BASE_URL, /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ trace: 'on-first-retry', @@ -39,19 +40,21 @@ export default defineConfig({ projects: [ { name: 'chromium', - use: { ...devices['Desktop Chrome'] }, + use: { ...devices['Desktop Chrome'], + }, }, { name: 'firefox', use: { ...devices['Desktop Firefox'], - storageState:"./storageState.json" + storageState:"./storageState.json", }, }, { name: 'webkit', - use: { ...devices['Desktop Safari'] }, + use: { ...devices['Desktop Safari'] , + }, }, /* Test against mobile viewports. */ diff --git a/tests/helpers/deleteUser.ts b/test_util/deleteUser.ts similarity index 91% rename from tests/helpers/deleteUser.ts rename to test_util/deleteUser.ts index dce96b7..92f2b69 100644 --- a/tests/helpers/deleteUser.ts +++ b/test_util/deleteUser.ts @@ -36,7 +36,7 @@ export async function deleteUserByEmail(email: string): Promise { console.error(`UID is null`); return false; } - const data = await deleteUser(uid); - console.log(`Successfully delete user with email: ${email}`); + await deleteUser(uid); + console.log(`Successfully delete user with email: ${email} and UID: ${uid}`); return true; } diff --git a/tests/helpers/getUser.ts b/test_util/getUser.ts similarity index 100% rename from tests/helpers/getUser.ts rename to test_util/getUser.ts diff --git a/test_util/global-setup.ts b/test_util/global-setup.ts index 60e4443..01ee2ff 100644 --- a/test_util/global-setup.ts +++ b/test_util/global-setup.ts @@ -1,24 +1,24 @@ import { firefox, FullConfig } from '@playwright/test'; async function globalSetup(config: FullConfig) { - const email = process.env.NEXT_PUBLIC_DUMMY_EMAIL; - const password = process.env.NEXT_PUBLIC_DUMMY_PASSWORD; - const baseUrl = 'http://127.0.0.1:3000'; + console.log('globalizing...'); + const email = process.env.NEXT_PUBLIC_TEST_USER_EMAIL; + const password = process.env.NEXT_PUBLIC_TEST_USER_PASSWORD; if (!email || !password) { - throw new Error('NEXT_PUBLIC_DUMMY_EMAIL and NEXT_PUBLIC_DUMMY_PASSWORD must be set'); + throw new Error('NEXT_PUBLIC_TEST_USER_EMAIL and NEXT_PUBLIC_TEST_USER_PASSWORD must be set'); } const browser = await firefox.launch(); const page = await browser.newPage(); - await page.goto(baseUrl + '/auth'); + + console.log('signing up user...'); + await page.goto(config.projects[0].use.baseURL + '/auth/signup'); await page.fill('id=email', email); await page.fill('id=password', password); - await Promise.all([ - page.waitForURL(baseUrl), - page.click('id=login') - ]); - await page.context().storageState({ path: 'storageState.json' }); + await page.fill('id=confirmPassword', password); + await page.click('id=signup') + await browser.close(); } diff --git a/test_util/global-teardown.ts b/test_util/global-teardown.ts new file mode 100644 index 0000000..d12158e --- /dev/null +++ b/test_util/global-teardown.ts @@ -0,0 +1,13 @@ +import { deleteUserByEmail } from './deleteUser'; + +async function globalTeardown() { + const email = process.env.NEXT_PUBLIC_TEST_USER_EMAIL; + + if (!email) { + throw new Error('NEXT_PUBLIC_TEST_USER_EMAIL must be set'); + } + console.log('deleting user...'); + await deleteUserByEmail(email); +} + +export default globalTeardown; \ No newline at end of file diff --git a/tests/helpers/dropdownUtils.ts b/tests/helpers/dropdownUtils.ts index 9208ce0..a257994 100644 --- a/tests/helpers/dropdownUtils.ts +++ b/tests/helpers/dropdownUtils.ts @@ -1,11 +1,9 @@ -// dropdownUtils.ts import { Page, Locator } from '@playwright/test'; export const selectFirstOption = async (page: Page, triggerLocator: Locator) => { let selected = false while (!selected) { try { - await triggerLocator.hover(); await triggerLocator.click({ force: true }); // Select the first available option @@ -16,9 +14,7 @@ export const selectFirstOption = async (page: Page, triggerLocator: Locator) => const optionText = await firstOption.textContent(); console.log(`${optionText}`); await firstOption.click(); - console.log("Selected."); - - selected = true; + selected = true } catch (error) { console.log("Retrying as the combobox disappeared."); await page.waitForTimeout(100); diff --git a/tests/helpers/login.ts b/tests/helpers/login.ts new file mode 100644 index 0000000..dea04eb --- /dev/null +++ b/tests/helpers/login.ts @@ -0,0 +1,26 @@ +import { Page } from '@playwright/test'; + +export const login = async (page: Page, role: 'user' | 'admin') => { + const email = role === 'user' ? process.env.NEXT_PUBLIC_TEST_USER_EMAIL : process.env.NEXT_PUBLIC_ADMIN_EMAIL; + const password = role === 'user' ? process.env.NEXT_PUBLIC_TEST_USER_PASSWORD : process.env.NEXT_PUBLIC_ADMIN_PASSWORD; + + if (!email || !password) { + throw new Error(`${role === 'user' ? 'User' : 'Admin'} credentials must be set`); + } + + await page.goto('/'); + const isLoginPage = await page.locator('id=login').isVisible(); + if (!isLoginPage) { + console.log(`Logging out current session...`); + await page.evaluate(() => { + localStorage.clear(); + sessionStorage.clear(); + }); + } + + console.log(`Logging in as ${role}...`); + await page.goto('/auth'); + await page.fill('id=email', email); + await page.fill('id=password', password); + await page.click('id=login'); +}; diff --git a/tests/test-01-business-apply.spec.ts b/tests/test-01-business-apply.spec.ts new file mode 100644 index 0000000..59debe6 --- /dev/null +++ b/tests/test-01-business-apply.spec.ts @@ -0,0 +1,20 @@ +import { test } from "@playwright/test"; +import { login } from "./helpers/login"; +import { selectFirstOption } from "./helpers/dropdownUtils"; + +test("test", async ({ page }) => { + await login(page, "user"); + await page.getByRole('button', { name: 'Businesses' }).hover(); + await page.getByRole("link", { name: "Business Apply to raise on on" }).click(); + + await selectFirstOption(page, page.locator("button").filter({ hasText: "Select an industry" })); + await selectFirstOption(page, page.locator("button").filter({ hasText: "Select a country" })); + await page.getByPlaceholder("$").fill("999998"); + await page.getByRole("button", { name: "Yes" }).first().click(); + await page.getByRole("button", { name: "Yes" }).nth(1).click(); + await page.getByRole("button", { name: "Yes" }).nth(2).click(); + await page.getByPlaceholder('https:// ').fill('https://www.test.md'); + await selectFirstOption(page, page.locator("button").filter({ hasText: "Select" })); + await page.locator("#companyName").fill("kasetsart"); + await page.getByRole('button', { name: 'Submit application' }).click(); +}); diff --git a/tests/test-03-project-apply.spec.ts b/tests/test-03-project-apply.spec.ts new file mode 100644 index 0000000..e8653a6 --- /dev/null +++ b/tests/test-03-project-apply.spec.ts @@ -0,0 +1,36 @@ +// import { test } from '@playwright/test'; +// import { selectFirstOption } from './helpers/dropdownUtils'; +// import { login } from './helpers/login'; +// import path from 'path'; +// +// test('test', async ({ page }) => { +// await login(page,'user') +// await page.getByRole('button', { name: 'Projects' }).hover(); +// await page.getByRole('link', { name: 'Projects Start your new' }).click(); +// +// const projectName = page.locator('#projectName') +// await projectName.pressSequentially('DummyTester'); +// await projectName.click(); +// +// const img = path.join(__dirname, 'mockup', '1x1.png'); +// await page.locator('#projectLogo').click(); +// await page.locator('#projectLogo').setInputFiles(img); +// await page.locator('#projectPhotos').click(); +// await page.locator('#projectPhotos').setInputFiles(img); +// +// const projectTypeButton = page.locator('button').filter({ hasText: 'Select a Project type' }); +// await selectFirstOption(page, projectTypeButton); +// +// await page.locator('#shortDescription').fill('0123456789'); +// await page.getByPlaceholder('https:// ').fill('https://www.test.md'); +// await page.getByPlaceholder('$ 500').fill('499'); +// await page.getByPlaceholder('$ 1,000,000').fill('99999999'); +// await page.locator('#deadline').fill('2024-11-29T21:19'); +// +// const tag = page.getByRole('combobox').nth(1); +// await selectFirstOption(page, tag); +// +// await projectName.pressSequentially('1234'); +// +// await page.getByRole('button', { name: 'Submit application' }).click(); +// }); diff --git a/tests/test-1-project-apply.spec.ts b/tests/test-1-project-apply.spec.ts deleted file mode 100644 index 6d78c6e..0000000 --- a/tests/test-1-project-apply.spec.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { test } from '@playwright/test'; -import { selectFirstOption } from './helpers/dropdownUtils'; -import path from 'path'; - -test('test', async ({ page }) => { - await page.goto("http://127.0.0.1:3000/"); - await page.getByRole('button', { name: 'Projects' }).hover(); - await page.getByRole('link', { name: 'Projects Start your new' }).click(); - - const projectName = page.locator('#projectName') - await projectName.pressSequentially('DummyTester'); - await projectName.click(); - - const img = path.join(__dirname, 'mockup', '1x1.png'); - await page.locator('#projectLogo').click(); - await page.locator('#projectLogo').setInputFiles(img); - await page.locator('#projectPhotos').click(); - await page.locator('#projectPhotos').setInputFiles(img); - - const projectTypeButton = page.locator('button').filter({ hasText: 'Select a Project type' }); - await selectFirstOption(page, projectTypeButton); - - await page.locator('#shortDescription').fill('0123456789'); - await page.getByPlaceholder('https:// ').fill('https://www.test.md'); - await page.getByPlaceholder('$ 500').fill('499'); - await page.getByPlaceholder('$ 1,000,000').fill('99999999'); - await page.locator('#deadline').fill('2024-11-29T21:19'); - - const tag = page.getByRole('combobox').nth(1); - await selectFirstOption(page, tag); - - await projectName.pressSequentially('1234'); - - await page.getByRole('button', { name: 'Submit application' }).click(); -});