| """ |
| End-to-end tests with Playwright. |
| Tests critical user flows: register, login, search jobs, track application. |
| """ |
| import re |
| from playwright.sync_api import Page, expect |
|
|
|
|
| BASE_URL = "http://localhost:3000" |
|
|
|
|
| class TestAuthFlow: |
| """Test registration and login flows.""" |
|
|
| def test_register_new_user(self, page: Page): |
| page.goto(f"{BASE_URL}/register") |
|
|
| |
| page.fill('[id="name"]', "E2E Test User") |
| page.fill('[id="email"]', "e2e@example.com") |
| page.fill('[id="password"]', "testpass123") |
| page.fill('[id="confirm"]', "testpass123") |
|
|
| |
| page.click('button[type="submit"]') |
|
|
| |
| page.wait_for_url("**/jobs") |
| expect(page).to_have_url(re.compile(r"/jobs")) |
|
|
| def test_login_existing_user(self, page: Page): |
| page.goto(f"{BASE_URL}/login") |
|
|
| page.fill('[id="email"]', "demo@jobportal.dev") |
| page.fill('[id="password"]', "demo123") |
| page.click('button[type="submit"]') |
|
|
| page.wait_for_url("**/jobs") |
| expect(page).to_have_url(re.compile(r"/jobs")) |
|
|
| def test_login_invalid_credentials(self, page: Page): |
| page.goto(f"{BASE_URL}/login") |
|
|
| page.fill('[id="email"]', "wrong@example.com") |
| page.fill('[id="password"]', "wrongpass") |
| page.click('button[type="submit"]') |
|
|
| |
| expect(page.locator("text=Invalid")).to_be_visible() |
|
|
|
|
| class TestJobSearch: |
| """Test job search and filtering.""" |
|
|
| def test_search_jobs(self, page: Page): |
| |
| self._login(page) |
|
|
| |
| expect(page.locator("h1")).to_contain_text("Find Jobs") |
|
|
| |
| page.fill('input[placeholder*="Search"]', "Python") |
| page.click('button:has-text("Search")') |
|
|
| |
| page.wait_for_timeout(1000) |
|
|
| def test_filter_remote_jobs(self, page: Page): |
| self._login(page) |
|
|
| |
| page.select_option('select[aria-label="Remote"]', "remote") |
| page.wait_for_timeout(500) |
|
|
| def test_view_job_detail(self, page: Page): |
| self._login(page) |
|
|
| |
| first_job = page.locator("a[href*='/jobs/']").first |
| if first_job.is_visible(): |
| first_job.click() |
| page.wait_for_url("**/jobs/**") |
| expect(page.locator("h1")).to_be_visible() |
|
|
| def _login(self, page: Page): |
| page.goto(f"{BASE_URL}/login") |
| page.fill('[id="email"]', "demo@jobportal.dev") |
| page.fill('[id="password"]', "demo123") |
| page.click('button[type="submit"]') |
| page.wait_for_url("**/jobs") |
|
|
|
|
| class TestApplicationTracker: |
| """Test application tracking kanban.""" |
|
|
| def test_create_application(self, page: Page): |
| self._login(page) |
| page.goto(f"{BASE_URL}/applications") |
|
|
| |
| page.click('button:has-text("Add Application")') |
|
|
| |
| page.fill('input[placeholder*="Job Title"]', "E2E Test Engineer") |
| page.fill('input[placeholder*="Company"]', "TestCorp") |
| page.click('button:has-text("Add")') |
|
|
| |
| page.wait_for_timeout(1000) |
| expect(page.locator("text=E2E Test Engineer")).to_be_visible() |
|
|
| def test_kanban_columns_exist(self, page: Page): |
| self._login(page) |
| page.goto(f"{BASE_URL}/applications") |
|
|
| expect(page.locator("text=Wishlist")).to_be_visible() |
| expect(page.locator("text=Applied")).to_be_visible() |
| expect(page.locator("text=Interviewing")).to_be_visible() |
|
|
| def _login(self, page: Page): |
| page.goto(f"{BASE_URL}/login") |
| page.fill('[id="email"]', "demo@jobportal.dev") |
| page.fill('[id="password"]', "demo123") |
| page.click('button[type="submit"]') |
| page.wait_for_url("**/jobs") |
|
|
|
|
| class TestResumeBuilder: |
| """Test resume management.""" |
|
|
| def test_resume_page_loads(self, page: Page): |
| self._login(page) |
| page.goto(f"{BASE_URL}/resume") |
|
|
| expect(page.locator("h1")).to_contain_text("Resume Builder") |
| |
| expect(page.locator("text=Simple ATS")).to_be_visible() |
|
|
| def test_create_resume(self, page: Page): |
| self._login(page) |
| page.goto(f"{BASE_URL}/resume") |
|
|
| page.click('button:has-text("New Resume")') |
| page.fill('input[placeholder*="Software Engineer"]', "My Test Resume") |
| page.click('button:has-text("Create")') |
|
|
| page.wait_for_timeout(1000) |
|
|
| def _login(self, page: Page): |
| page.goto(f"{BASE_URL}/login") |
| page.fill('[id="email"]', "demo@jobportal.dev") |
| page.fill('[id="password"]', "demo123") |
| page.click('button[type="submit"]') |
| page.wait_for_url("**/jobs") |
|
|
|
|
| class TestProfile: |
| """Test profile editing.""" |
|
|
| def test_profile_page_loads(self, page: Page): |
| self._login(page) |
| page.goto(f"{BASE_URL}/profile") |
|
|
| expect(page.locator("h1")).to_contain_text("Profile") |
| expect(page.locator("text=Basics")).to_be_visible() |
| expect(page.locator("text=Experience")).to_be_visible() |
|
|
| def test_edit_profile_basics(self, page: Page): |
| self._login(page) |
| page.goto(f"{BASE_URL}/profile") |
|
|
| page.fill('input[placeholder*="Senior Software"]', "Full Stack Developer") |
| page.fill('input[placeholder*="San Francisco"]', "New York, NY") |
| page.click('button:has-text("Save")') |
|
|
| page.wait_for_timeout(1000) |
|
|
| def _login(self, page: Page): |
| page.goto(f"{BASE_URL}/login") |
| page.fill('[id="email"]', "demo@jobportal.dev") |
| page.fill('[id="password"]', "demo123") |
| page.click('button[type="submit"]') |
| page.wait_for_url("**/jobs") |
|
|