@trevordsouzabrite/test-package 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/agents/playwright-test-generator.md +85 -0
- package/.claude/agents/playwright-test-healer.md +45 -0
- package/.claude/agents/playwright-test-planner.md +52 -0
- package/.claude/prompts/playwright-test-coverage.md +31 -0
- package/.claude/prompts/playwright-test-generate.md +12 -0
- package/.claude/prompts/playwright-test-heal.md +6 -0
- package/.claude/prompts/playwright-test-plan.md +12 -0
- package/.claude/settings.local.json +31 -0
- package/.github/agents/playwright-test-generator.agent.md +113 -0
- package/.github/agents/playwright-test-healer.agent.md +70 -0
- package/.github/agents/playwright-test-planner.agent.md +82 -0
- package/.github/prompts/playwright-test-coverage.prompt.md +31 -0
- package/.github/prompts/playwright-test-generate.prompt.md +12 -0
- package/.github/prompts/playwright-test-heal.prompt.md +6 -0
- package/.github/prompts/playwright-test-plan.prompt.md +9 -0
- package/.github/workflows/copilot-setup-steps.yml +34 -0
- package/.github/workflows/playwright-healer-agent.yml +140 -0
- package/.github/workflows/playwright.yml +40 -0
- package/.mcp.json +13 -0
- package/.vscode/extensions.json +6 -0
- package/.vscode/mcp.json +13 -0
- package/.vscode/settings.example.json +15 -0
- package/bitbucket-pipelines.yml +86 -0
- package/lib/WebActions.ts +107 -0
- package/package.json +33 -0
- package/pageRepository/ApplicantPage.ts +1171 -0
- package/pageRepository/CreateApplicationPage.ts +1736 -0
- package/playwright/.auth/user.json +0 -0
- package/specs/Applicant Create Application Page Test Plan.md +440 -0
- package/specs/Applicant Dashboard Page Test Plan.md +74 -0
- package/specs/Applicant Forgot Password Page Test Plan.md +112 -0
- package/specs/Applicant Help Page Test Plan.md +369 -0
- package/specs/Applicant Landing Page Test Plan.md +42 -0
- package/specs/Applicant Login Page Test Plan.md +116 -0
- package/specs/Applicant My Applications Page Test Plan.md +558 -0
- package/specs/Applicant My Medical Coverage Page Test Plan.md +689 -0
- package/specs/Applicant Privacy Policy Page Test Plan.md +196 -0
- package/specs/Applicant Resources Page Test Plan.md +107 -0
- package/specs/Applicant Self Register Page Test Plan.md +190 -0
- package/specs/README.md +3 -0
- package/test-data/Sample.png +0 -0
- package/test-data/createApplication/formData.json +42 -0
- package/test-data/createApplication/textMessages.json +52 -0
- package/test-data/forgotPassword/email.json +5 -0
- package/test-data/forgotPassword/textMessages.json +5 -0
- package/test-data/help/textContent.json +48 -0
- package/test-data/login/invalidUsernamePassword.json +4 -0
- package/test-data/login/textMessages.json +5 -0
- package/test-data/privacyPolicy/textContent.json +25 -0
- package/test-data/selfRegister/mailingAddressStates.json +21 -0
- package/test-data/selfRegister/registrationFieldData.json +13 -0
- package/test-data/selfRegister/suffix.json +3 -0
- package/test-data/selfRegister/textMessages.json +13 -0
- package/test-data/test-data.zip +0 -0
- package/tests/ApplicantCreateApplicationPageTest.spec.ts +1452 -0
- package/tests/ApplicantDashboardPageTest.spec.ts +74 -0
- package/tests/ApplicantForgotPasswordPageTest.spec.ts +88 -0
- package/tests/ApplicantHelpPageTest.spec.ts +468 -0
- package/tests/ApplicantLandingPageTest.spec.ts +33 -0
- package/tests/ApplicantLoginPageTest.spec.ts +117 -0
- package/tests/ApplicantMyApplicationsPageTest.spec.ts +516 -0
- package/tests/ApplicantMyMedicalCoveragePageTest.spec.ts +470 -0
- package/tests/ApplicantPrivacyPolicyPageTest.spec.ts +188 -0
- package/tests/ApplicantResourcesPageTest.spec.ts +117 -0
- package/tests/ApplicantSelfRegisterPageTest.spec.ts +254 -0
- package/tests/auth.setup.ts +42 -0
- package/tests/authState.ts +15 -0
- package/tests/example.spec.ts +18 -0
- package/tests/seed.spec.ts +7 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// spec: specs/Applicant Landing Page Test Plan.md
|
|
2
|
+
// seed: seed.spec.ts
|
|
3
|
+
|
|
4
|
+
import { test, expect } from '@playwright/test';
|
|
5
|
+
import { ApplicantPage } from '@pages/ApplicantPage';
|
|
6
|
+
import { expectApplicantFooterVisible } from '@lib/ApplicantPortalAssertions';
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
test.describe('Applicant Landing Page Test Validations', () => {
|
|
10
|
+
let applicantPage: ApplicantPage;
|
|
11
|
+
test.beforeEach(async ({ page }) => {
|
|
12
|
+
applicantPage = new ApplicantPage(page);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test('Verify different sections of the applicant landing page @no-auth @smoke ', async ({ page }) => {
|
|
16
|
+
// 1. Navigate to landing page URL
|
|
17
|
+
await applicantPage.gotoURL('/polkphpapplicant/s/?language=en_US');
|
|
18
|
+
|
|
19
|
+
// 2. Validate different parts of the page are visible or not
|
|
20
|
+
// Header Section Validations
|
|
21
|
+
await expect.soft(applicantPage.Logo_Img).toBeVisible();
|
|
22
|
+
await expect.soft(applicantPage.Login_Btn).toBeVisible();
|
|
23
|
+
|
|
24
|
+
//Heading & Content Section Validations
|
|
25
|
+
await expect.soft(applicantPage.WelcomeHeading_Text).toBeVisible();
|
|
26
|
+
const text = await applicantPage.Instructions_Text.textContent();
|
|
27
|
+
console.log(`Instructions Text: ${text}`);
|
|
28
|
+
await expect.soft(applicantPage.Banner_Img).toBeVisible();
|
|
29
|
+
|
|
30
|
+
await expectApplicantFooterVisible(applicantPage);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
});
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// spec: specs/Applicant Login Page Test Plan.md
|
|
2
|
+
// seed: seed.spec.ts
|
|
3
|
+
|
|
4
|
+
import { test, expect } from '@playwright/test';
|
|
5
|
+
import { ApplicantPage } from '@pages/ApplicantPage';
|
|
6
|
+
import { testConfig } from 'testConfig';
|
|
7
|
+
import { WebActions } from '@lib/WebActions';
|
|
8
|
+
import { expectApplicantFooterVisible } from '@lib/ApplicantPortalAssertions';
|
|
9
|
+
import loginData from '../test-data/login/invalidUsernamePassword.json';
|
|
10
|
+
import loginTextData from '../test-data/login/textMessages.json';
|
|
11
|
+
|
|
12
|
+
test.describe('Applicant Login Page Tests', () => {
|
|
13
|
+
let applicantPage: ApplicantPage;
|
|
14
|
+
let webActions: WebActions;
|
|
15
|
+
test.beforeEach(async ({ page }) => {
|
|
16
|
+
applicantPage = new ApplicantPage(page);
|
|
17
|
+
webActions = new WebActions(page, page.context());
|
|
18
|
+
await applicantPage.gotoURL('/polkphpapplicant/s/?language=en_US');
|
|
19
|
+
await webActions.waitForElementAttached(applicantPage.Login_Btn);
|
|
20
|
+
//Navigate to the login page by clicking the login button on the header
|
|
21
|
+
await applicantPage.clickLoginButton();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test('Verify the required elements of the login page are visible @no-auth', async ({ page }) => {
|
|
25
|
+
//Verify the URL contains the expected path for the login page
|
|
26
|
+
expect.soft(page.url()).toContain('loginpage');
|
|
27
|
+
|
|
28
|
+
//Verify the login message, fields and buttons are visible on the login page
|
|
29
|
+
expect.soft(await applicantPage.LoginText_Msg.textContent()).toContain(loginTextData.loginText);
|
|
30
|
+
await expect.soft(applicantPage.UsernameField_Lbl).toBeVisible();
|
|
31
|
+
await expect.soft(applicantPage.UsernameField_Input).toBeVisible();
|
|
32
|
+
await expect.soft(applicantPage.PasswordField_Lbl).toBeVisible();
|
|
33
|
+
await expect.soft(applicantPage.PasswordField_Input).toBeVisible();
|
|
34
|
+
await expect.soft(applicantPage.LoginSubmit_Btn).toBeVisible();
|
|
35
|
+
await expect.soft(applicantPage.ForgotPassword_Lnk).toBeVisible();
|
|
36
|
+
await expect.soft(applicantPage.SignUp_Lnk).toBeVisible();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test('Verify Error Message is displayed for Blank Username for submission @no-auth', async ({ page }) => {
|
|
40
|
+
//Click on the login submit button without entering any credentials
|
|
41
|
+
await applicantPage.clickLoginSubmitButton();
|
|
42
|
+
const validationMessage = await webActions.getErrorMessage(applicantPage.UsernameField_Input);
|
|
43
|
+
expect.soft(validationMessage).toBe(loginTextData.requiredFieldError);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test('Verify Error Message is displayed for Blank Password for submission @no-auth', async ({ page }) => {
|
|
47
|
+
//Enter only the username and click on the login submit button
|
|
48
|
+
await applicantPage.UsernameField_Input.fill(testConfig.login);
|
|
49
|
+
await applicantPage.clickLoginSubmitButton();
|
|
50
|
+
|
|
51
|
+
const validationMessage = await webActions.getErrorMessage(applicantPage.PasswordField_Input);
|
|
52
|
+
expect.soft(validationMessage).toBe(loginTextData.requiredFieldError);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test('Verify the password field masks the input @no-auth', async ({ page }) => {
|
|
56
|
+
//Verify the password field has the type attribute set to "password" which masks the input
|
|
57
|
+
const inputType = await applicantPage.PasswordField_Input.getAttribute('type');
|
|
58
|
+
expect(inputType).toBe('password');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('Verify the error message is getting displayed for incorrect username @no-auth', async ({ page }) => {
|
|
62
|
+
//Enter invalid credentials and click on the login submit button
|
|
63
|
+
await applicantPage.UsernameField_Input.fill(loginData.username);
|
|
64
|
+
await applicantPage.PasswordField_Input.fill(loginData.password);
|
|
65
|
+
await applicantPage.clickLoginSubmitButton();
|
|
66
|
+
expect.soft(await applicantPage.ErrorMessage_Text.textContent()).toContain(loginTextData.incorrectUsernamePasswordError);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('Verify the error message is getting displayed for incorrect password @no-auth', async ({ page }) => {
|
|
70
|
+
//Enter invalid credentials and click on the login submit button
|
|
71
|
+
await applicantPage.UsernameField_Input.fill(testConfig.login);
|
|
72
|
+
const isUsernameFieldEmpty = await webActions.checkIfFieldIsEmptyOrNot(applicantPage.UsernameField_Input);
|
|
73
|
+
expect.soft(isUsernameFieldEmpty).toBe(false);
|
|
74
|
+
await applicantPage.PasswordField_Input.fill(loginData.password);
|
|
75
|
+
const isPasswordFieldEmpty = await webActions.checkIfFieldIsEmptyOrNot(applicantPage.PasswordField_Input);
|
|
76
|
+
expect.soft(isPasswordFieldEmpty).toBe(false);
|
|
77
|
+
await applicantPage.clickLoginSubmitButton();
|
|
78
|
+
expect.soft(await applicantPage.ErrorMessage_Text.textContent()).toContain(loginTextData.incorrectUsernamePasswordError);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test('Verify the applicant is logged-in for correct credentials @no-auth', async ({ page }) => {
|
|
82
|
+
//Enter correct credentials and click on the login submit button
|
|
83
|
+
await applicantPage.UsernameField_Input.fill(testConfig.login);
|
|
84
|
+
const isUsernameFieldEmpty = await webActions.checkIfFieldIsEmptyOrNot(applicantPage.UsernameField_Input);
|
|
85
|
+
expect.soft(isUsernameFieldEmpty).toBe(false);
|
|
86
|
+
await applicantPage.PasswordField_Input.fill(testConfig.password);
|
|
87
|
+
const isPasswordFieldEmpty = await webActions.checkIfFieldIsEmptyOrNot(applicantPage.PasswordField_Input);
|
|
88
|
+
expect.soft(isPasswordFieldEmpty).toBe(false);
|
|
89
|
+
await applicantPage.clickLoginSubmitButton();
|
|
90
|
+
await applicantPage.completeConsentFormUpdateIfRequired();
|
|
91
|
+
await applicantPage.Home_Lnk.waitFor({ state: 'attached' });
|
|
92
|
+
await expect(applicantPage.Logout_Btn).toBeVisible();
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test('Verify navigation to Forgot Password @no-auth', async ({ page }) => {
|
|
96
|
+
//Click on the login submit button without entering any credentials
|
|
97
|
+
await applicantPage.clickForgotPasswordLink();
|
|
98
|
+
//Verify the URL is correct & Form Title is visible
|
|
99
|
+
expect.soft(page.url()).toContain('polkphpapplicant/s/forgetpassword');
|
|
100
|
+
await expect.soft(applicantPage.ForgotPasswordTitle_Text).toBeVisible();
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test('Verify navigation to Self Register @no-auth', async ({ page }) => {
|
|
104
|
+
//Click on the login submit button without entering any credentials
|
|
105
|
+
await applicantPage.clickSignUpLink();
|
|
106
|
+
//Verify the URL is correct & Form Title is visible
|
|
107
|
+
expect.soft(page.url()).toContain('polkphpapplicant/s/polkselfregister');
|
|
108
|
+
await expect.soft(applicantPage.SelfRegisterFormTitle_Text).toBeVisible();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test('Verify global chrome (header and footer) on the login page @no-auth', async ({ page }) => {
|
|
112
|
+
// Header Section Validations
|
|
113
|
+
await expect.soft(applicantPage.Logo_Img.first()).toBeVisible();
|
|
114
|
+
await expect.soft(applicantPage.Login_Btn).toBeVisible();
|
|
115
|
+
await expectApplicantFooterVisible(applicantPage);
|
|
116
|
+
});
|
|
117
|
+
});
|
|
@@ -0,0 +1,516 @@
|
|
|
1
|
+
// spec: specs/Applicant My Applications Page Test Plan.md
|
|
2
|
+
// seed: tests/seed.spec.ts
|
|
3
|
+
|
|
4
|
+
import { test, expect } from '@playwright/test';
|
|
5
|
+
import AxeBuilder from '@axe-core/playwright';
|
|
6
|
+
import { ApplicantPage } from '@pages/ApplicantPage';
|
|
7
|
+
import { WebActions } from '@lib/WebActions';
|
|
8
|
+
import { expectApplicantAuthenticatedHeaderFooterVisible } from '@lib/ApplicantPortalAssertions';
|
|
9
|
+
import { testConfig } from '../testConfig';
|
|
10
|
+
|
|
11
|
+
const MY_APPLICATIONS_PATH = '/polkphpapplicant/s/careprogram/CareProgram/Default';
|
|
12
|
+
|
|
13
|
+
function parseApplicationDate(dateText: string): number {
|
|
14
|
+
const parsed = Date.parse(dateText.replace(',', ''));
|
|
15
|
+
return Number.isNaN(parsed) ? 0 : parsed;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function isSorted(values: number[]): boolean {
|
|
19
|
+
if (values.length < 2) {
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
const ascending = values.every((value, index) => index === 0 || values[index - 1] <= value);
|
|
23
|
+
const descending = values.every((value, index) => index === 0 || values[index - 1] >= value);
|
|
24
|
+
return ascending || descending;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
test.describe('Applicant My Applications Page Tests', () => {
|
|
28
|
+
test.describe('Authenticated My Applications List', () => {
|
|
29
|
+
test.skip(process.env.AUTH !== 'true', 'Authenticated My Applications tests require AUTH=true and playwright/.auth/user.json.');
|
|
30
|
+
test.describe.configure({ mode: 'serial', timeout: 120_000 });
|
|
31
|
+
|
|
32
|
+
let applicantPage: ApplicantPage;
|
|
33
|
+
let webActions: WebActions;
|
|
34
|
+
|
|
35
|
+
test.beforeEach(async ({ page }) => {
|
|
36
|
+
applicantPage = new ApplicantPage(page);
|
|
37
|
+
webActions = new WebActions(page, page.context());
|
|
38
|
+
await applicantPage.gotoMyApplicationsPage();
|
|
39
|
+
await webActions.waitForElementAttached(applicantPage.Logout_Btn);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('TC-01 - Authenticated My Applications List Loads @auth ', async ({ page }) => {
|
|
43
|
+
// 1. Navigate to the My Applications URL with an authenticated applicant session.
|
|
44
|
+
await applicantPage.waitForMyApplicationsPageReady();
|
|
45
|
+
// 2. Verify the browser title is Applications.
|
|
46
|
+
await expect.soft(page).toHaveTitle(/Applications/i);
|
|
47
|
+
// 3. Verify the page shows the Applications heading and Recently Viewed list label.
|
|
48
|
+
await expect.soft(applicantPage.MyApplicationsHeading_Text).toBeVisible();
|
|
49
|
+
await expect.soft(applicantPage.MyApplicationsRecentlyViewed_Text).toBeVisible();
|
|
50
|
+
// 4. Verify the list summary shows an item count and recently updated timestamp.
|
|
51
|
+
await expect.soft(applicantPage.MyApplicationsListSummary_Text).toContainText(/items?|Updated/i);
|
|
52
|
+
// 5. Verify the table/list view container is visible.
|
|
53
|
+
await expect.soft(applicantPage.MyApplications_Table).toBeVisible();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test('TC-02 - Header And Footer Are Visible @auth', async () => {
|
|
57
|
+
// 1. Open the My Applications page.
|
|
58
|
+
await applicantPage.waitForMyApplicationsPageReady();
|
|
59
|
+
// 2-4. Verify authenticated header links, Logout, and footer content.
|
|
60
|
+
await expectApplicantAuthenticatedHeaderFooterVisible(applicantPage);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test('TC-03 - List View Table Columns Are Visible @auth', async () => {
|
|
64
|
+
// 1. Open the My Applications page.
|
|
65
|
+
await applicantPage.waitForMyApplicationsPageReady();
|
|
66
|
+
// 2. Verify the visible columns.
|
|
67
|
+
await expect.soft(applicantPage.MyApplicationsColumnHeaders_Block).toContainText('Name');
|
|
68
|
+
await expect.soft(applicantPage.MyApplicationsColumnHeaders_Block).toContainText('Last Modified Date');
|
|
69
|
+
await expect.soft(applicantPage.MyApplicationsColumnHeaders_Block).toContainText('Last Modified By');
|
|
70
|
+
await expect.soft(applicantPage.MyApplicationsColumnHeaders_Block).toContainText('Parent Program');
|
|
71
|
+
await expect.soft(applicantPage.MyApplicationsColumnHeaders_Block).toContainText('Program Sponsor');
|
|
72
|
+
await expect.soft(applicantPage.MyApplicationsColumnHeaders_Block).toContainText('Action');
|
|
73
|
+
// 3. Verify row action controls are present for visible records.
|
|
74
|
+
await expect.soft(applicantPage.MyApplicationsFirstRowActions_Btn).toBeVisible();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test('TC-04 - Existing Application Rows Display Expected Data @auth', async () => {
|
|
78
|
+
// 1. Open the My Applications page for an applicant that has application records.
|
|
79
|
+
await applicantPage.waitForMyApplicationsPageReady();
|
|
80
|
+
// 2. Verify at least one application row is visible.
|
|
81
|
+
expect.soft(await applicantPage.getMyApplicationsVisibleRowCount()).toBeGreaterThan(0);
|
|
82
|
+
// 3. Verify the application name is rendered as a link.
|
|
83
|
+
await expect.soft(applicantPage.MyApplicationsFirstApplication_Lnk).toBeVisible();
|
|
84
|
+
// 4. Verify Last Modified Date displays a valid date/time value when populated.
|
|
85
|
+
const visibleDates = await applicantPage.getMyApplicationsVisibleLastModifiedDateTexts();
|
|
86
|
+
expect.soft(visibleDates.length).toBeGreaterThan(0);
|
|
87
|
+
for (const visibleDate of visibleDates) {
|
|
88
|
+
expect.soft(parseApplicationDate(visibleDate), `Expected valid row date: ${visibleDate}`).toBeGreaterThan(0);
|
|
89
|
+
}
|
|
90
|
+
// 5. Verify blank optional columns do not break row layout.
|
|
91
|
+
await expect.soft(applicantPage.MyApplicationsFirstRow_Block).toBeVisible();
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test('TC-05 - Search Returns Matching Applications @auth', async () => {
|
|
95
|
+
// 1. Open the My Applications page.
|
|
96
|
+
await applicantPage.waitForMyApplicationsPageReady();
|
|
97
|
+
// 2. Enter a known application name, for example John, in Search this list....
|
|
98
|
+
await applicantPage.searchMyApplicationsList('John');
|
|
99
|
+
// 3. Press Enter. (done by searchMyApplicationsList)
|
|
100
|
+
// 4. Wait for the list to refresh.
|
|
101
|
+
await applicantPage.waitForMyApplicationsListToSettle();
|
|
102
|
+
// Verify matching application rows are displayed and the clear-search control appears.
|
|
103
|
+
await expect.soft(applicantPage.MyApplicationsClearSearch_Btn).toBeVisible();
|
|
104
|
+
expect.soft((await applicantPage.getMyApplicationsRowsText()).join(' ')).toContain('John');
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test('TC-06 - Search With No Matches Shows Empty State @auth', async () => {
|
|
108
|
+
// 1. Open the My Applications page.
|
|
109
|
+
await applicantPage.waitForMyApplicationsPageReady();
|
|
110
|
+
// 2. Search for a value that should not match any application.
|
|
111
|
+
await applicantPage.searchMyApplicationsList('DefinitelyNoSuchApplication999');
|
|
112
|
+
// 3. Press Enter. (done by searchMyApplicationsList)
|
|
113
|
+
// Verify list summary, empty state, clear control, and helper text.
|
|
114
|
+
await expect.soft(applicantPage.MyApplicationsListSummary_Text).toContainText(/0 items/i);
|
|
115
|
+
await expect.soft(applicantPage.MyApplicationsNoItems_Text).toBeVisible();
|
|
116
|
+
await expect.soft(applicantPage.MyApplicationsClearSearch_Btn).toBeVisible();
|
|
117
|
+
await expect.soft(applicantPage.MyApplicationsSearchHelper_Text).toBeVisible();
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test('TC-07 - Clear Search Restores Default List @auth', async () => {
|
|
121
|
+
// 1. Perform a no-match search.
|
|
122
|
+
await applicantPage.searchMyApplicationsList('DefinitelyNoSuchApplication999');
|
|
123
|
+
await expect.soft(applicantPage.MyApplicationsNoItems_Text).toBeVisible();
|
|
124
|
+
// 2. Click Clear.
|
|
125
|
+
await applicantPage.clearMyApplicationsSearch();
|
|
126
|
+
// 3. Wait for the list to refresh.
|
|
127
|
+
await applicantPage.waitForMyApplicationsListToSettle();
|
|
128
|
+
// Verify default Recently Viewed list is restored.
|
|
129
|
+
await expect.soft(applicantPage.MyApplicationsNoItems_Text).not.toBeVisible();
|
|
130
|
+
await expect.soft(applicantPage.MyApplicationsFirstApplication_Lnk).toBeVisible();
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test('TC-08 - Sort By Last Modified Date @auth', async () => {
|
|
134
|
+
// 1. Open the My Applications page.
|
|
135
|
+
await applicantPage.waitForMyApplicationsPageReady();
|
|
136
|
+
// 2. Click the Last Modified Date column sort control.
|
|
137
|
+
await applicantPage.clickMyApplicationsLastModifiedDateSort();
|
|
138
|
+
// 3. Verify the sorted indicator appears for the column.
|
|
139
|
+
expect.soft(await applicantPage.getMyApplicationsSortStateText()).toMatch(/ascending|descending|sorted|Last Modified Date/i);
|
|
140
|
+
// 4. Capture the first several visible row dates.
|
|
141
|
+
const dates = await applicantPage.getMyApplicationsVisibleLastModifiedDateTexts();
|
|
142
|
+
const parsedDates = dates.map(parseApplicationDate).filter(Boolean);
|
|
143
|
+
expect.soft(parsedDates.length).toBeGreaterThan(0);
|
|
144
|
+
expect.soft(isSorted(parsedDates)).toBe(true);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
test('TC-09 - Sort By Name @auth', async () => {
|
|
148
|
+
// 1. Open the My Applications page.
|
|
149
|
+
await applicantPage.waitForMyApplicationsPageReady();
|
|
150
|
+
// 2. Click the Name column sort control.
|
|
151
|
+
await applicantPage.clickMyApplicationsNameSort();
|
|
152
|
+
const firstSortRows = await applicantPage.getMyApplicationsRowsText();
|
|
153
|
+
// 3. Verify the sort state changes.
|
|
154
|
+
expect.soft(firstSortRows.length).toBeGreaterThan(0);
|
|
155
|
+
// 4. Click the same column again.
|
|
156
|
+
await applicantPage.clickMyApplicationsNameSort();
|
|
157
|
+
const secondSortRows = await applicantPage.getMyApplicationsRowsText();
|
|
158
|
+
expect.soft(secondSortRows.length).toBeGreaterThan(0);
|
|
159
|
+
await expect.soft(applicantPage.MyApplicationsFirstApplication_Lnk).toBeVisible();
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
test('TC-10 - Column Action Menu Supports Text Display Options @auth', async () => {
|
|
163
|
+
// 1. Open the My Applications page.
|
|
164
|
+
await applicantPage.waitForMyApplicationsPageReady();
|
|
165
|
+
// 2. Click Show Last Modified Date Column Actions.
|
|
166
|
+
await applicantPage.openMyApplicationsLastModifiedDateColumnActions();
|
|
167
|
+
// 3. Verify the menu opens.
|
|
168
|
+
await expect.soft(applicantPage.MyApplicationsWrapText_MenuItem).toBeVisible();
|
|
169
|
+
// 4. Select Wrap text.
|
|
170
|
+
await applicantPage.MyApplicationsWrapText_MenuItem.click();
|
|
171
|
+
// 5. Reopen the menu and select Clip text.
|
|
172
|
+
await applicantPage.openMyApplicationsLastModifiedDateColumnActions();
|
|
173
|
+
await expect.soft(applicantPage.MyApplicationsClipText_MenuItem).toBeVisible();
|
|
174
|
+
await applicantPage.MyApplicationsClipText_MenuItem.click();
|
|
175
|
+
await expect.soft(applicantPage.MyApplicationsFirstRow_Block).toBeVisible();
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
test('TC-11 - Row Action Menu Opens For An Application @auth', async () => {
|
|
179
|
+
// 1. Open the My Applications page.
|
|
180
|
+
await applicantPage.waitForMyApplicationsPageReady();
|
|
181
|
+
// 2. Click the first row Show Actions control.
|
|
182
|
+
await applicantPage.openFirstMyApplicationsRowActions();
|
|
183
|
+
// 3. Verify the menu opens.
|
|
184
|
+
await expect.soft(applicantPage.MyApplicationsEdit_MenuItem).toBeVisible();
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
test('TC-12 - Edit Row Action Opens Editable Application State Without Submitting @auth', async () => {
|
|
188
|
+
// 1. Open the first row action menu.
|
|
189
|
+
await applicantPage.openFirstMyApplicationsRowActions();
|
|
190
|
+
// 2. Click Edit.
|
|
191
|
+
await applicantPage.MyApplicationsEdit_MenuItem.click();
|
|
192
|
+
// 3. Verify an edit form, modal, or editable record state appears.
|
|
193
|
+
await expect.soft(applicantPage.MyApplicationsEditDialog_Block).toBeVisible();
|
|
194
|
+
// 4. Close or cancel the edit state without saving.
|
|
195
|
+
await applicantPage.cancelOrCloseOpenDialog();
|
|
196
|
+
await expect.soft(applicantPage.MyApplications_Table).toBeVisible();
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
test('TC-13 - List View Selector Displays Available Views @auth', async () => {
|
|
200
|
+
// 1. Open the My Applications page.
|
|
201
|
+
await applicantPage.waitForMyApplicationsPageReady();
|
|
202
|
+
// 2. Click Select a List View: Applications.
|
|
203
|
+
await applicantPage.openMyApplicationsListViewSelector();
|
|
204
|
+
// 3. Verify available list views.
|
|
205
|
+
await expect.soft(applicantPage.MyApplicationsAllApplications_MenuItem).toBeVisible();
|
|
206
|
+
await expect.soft(applicantPage.MyApplicationsAppealedApplications_MenuItem).toBeVisible();
|
|
207
|
+
await expect.soft(applicantPage.MyApplicationsRecentlyViewed_MenuItem).toBeVisible();
|
|
208
|
+
await expect.soft(applicantPage.MyApplicationsWithPriorityIndicator_MenuItem).toBeVisible();
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
test('TC-14 - Switch To Each Available List View @auth', async () => {
|
|
212
|
+
// 1. Open the list view selector.
|
|
213
|
+
// 2. Select each available list view one at a time.
|
|
214
|
+
for (const listView of ['All Applications', 'Appealed Applications', 'Recently Viewed', 'With Priority Indicator'] as const) {
|
|
215
|
+
await applicantPage.selectMyApplicationsListView(listView);
|
|
216
|
+
// 3. Wait for the list to refresh after each selection.
|
|
217
|
+
await applicantPage.waitForMyApplicationsListToSettle();
|
|
218
|
+
// Verify each list view loads without an application error.
|
|
219
|
+
await expect.soft(applicantPage.MyApplications_Table).toBeVisible();
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
test('TC-15 - List View Controls Menu Behavior @auth', async ({ page }) => {
|
|
224
|
+
// 1. Open the My Applications page.
|
|
225
|
+
await applicantPage.waitForMyApplicationsPageReady();
|
|
226
|
+
// 2. Click List View Controls.
|
|
227
|
+
await applicantPage.openMyApplicationsListViewControls();
|
|
228
|
+
// 3. Verify menu options are displayed.
|
|
229
|
+
await expect.soft(applicantPage.MyApplicationsNew_MenuItem).toBeVisible();
|
|
230
|
+
await expect.soft(applicantPage.MyApplicationsClone_MenuItem).toBeVisible();
|
|
231
|
+
await expect.soft(applicantPage.MyApplicationsRename_MenuItem).toBeVisible();
|
|
232
|
+
await expect.soft(applicantPage.MyApplicationsSharingSettings_MenuItem).toBeVisible();
|
|
233
|
+
await expect.soft(applicantPage.MyApplicationsSelectFieldsToDisplay_MenuItem).toBeVisible();
|
|
234
|
+
await expect.soft(applicantPage.MyApplicationsDelete_MenuItem).toBeVisible();
|
|
235
|
+
await expect.soft(applicantPage.MyApplicationsResetColumnWidths_MenuItem).toBeVisible();
|
|
236
|
+
// 4. Dismiss the menu with Escape.
|
|
237
|
+
await page.keyboard.press('Escape');
|
|
238
|
+
await expect.soft(applicantPage.MyApplications_Table).toBeVisible();
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
test('TC-16 - Refresh List View @auth', async () => {
|
|
242
|
+
// 1. Open the My Applications page.
|
|
243
|
+
const beforeRefreshSummary = await applicantPage.getMyApplicationsListSummaryText();
|
|
244
|
+
// 2. Click Refresh.
|
|
245
|
+
await applicantPage.refreshMyApplicationsList();
|
|
246
|
+
// 3. Wait for the updated timestamp or loading state to complete.
|
|
247
|
+
const afterRefreshSummary = await applicantPage.getMyApplicationsListSummaryText();
|
|
248
|
+
expect.soft(afterRefreshSummary).toMatch(/items?|Updated/i);
|
|
249
|
+
expect.soft(afterRefreshSummary.length).toBeGreaterThan(0);
|
|
250
|
+
expect.soft(beforeRefreshSummary.length).toBeGreaterThan(0);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
test('TC-17 - Disabled Charts And Filters Controls @auth', async () => {
|
|
254
|
+
// 1. Open the My Applications page.
|
|
255
|
+
await applicantPage.waitForMyApplicationsPageReady();
|
|
256
|
+
// 2. Verify Charts button is disabled.
|
|
257
|
+
await expect.soft(applicantPage.MyApplicationsCharts_Btn).toBeDisabled();
|
|
258
|
+
// 3. Verify Filters button is disabled.
|
|
259
|
+
await expect.soft(applicantPage.MyApplicationsFilters_Btn).toBeDisabled();
|
|
260
|
+
// 4. Hover or inspect titles.
|
|
261
|
+
expect.soft(await applicantPage.getChartsUnavailableTitle()).toContain('Charts are not available for this list view');
|
|
262
|
+
expect.soft(await applicantPage.getFiltersUnavailableTitle()).toContain('Filters are not available for this list view');
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
test('TC-18 - Open Application Record Detail From List @auth ', async ({ page }) => {
|
|
266
|
+
// 1. Open the My Applications page.
|
|
267
|
+
await applicantPage.waitForMyApplicationsPageReady();
|
|
268
|
+
// 2. Click the first application name link.
|
|
269
|
+
const applicationName = await applicantPage.openFirstMyApplicationsRecord();
|
|
270
|
+
// 3. Wait for the record detail page to load.
|
|
271
|
+
await expect.soft(page).toHaveURL(/\/polkphpapplicant\/s\/careprogram\/[^/]+\/[^/?#]+/i);
|
|
272
|
+
await expect.soft(page).toHaveTitle(new RegExp(`Application:.*${applicationName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}`, 'i'));
|
|
273
|
+
await expect.soft(applicantPage.ApplicationRecordHeader_Text).toBeVisible();
|
|
274
|
+
await expect.soft(applicantPage.ApplicationRecordName_Heading).toBeVisible();
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
test('TC-19 - Application Record Summary Fields Are Visible @auth', async () => {
|
|
278
|
+
// 1. Open an application record detail page.
|
|
279
|
+
await applicantPage.openFirstMyApplicationsRecord();
|
|
280
|
+
// 2. Verify summary fields.
|
|
281
|
+
await expect.soft(applicantPage.ApplicationRecordStatus_Text).toBeVisible();
|
|
282
|
+
await expect.soft(applicantPage.ApplicationRecordApplicationTotalFPL_Text).toBeVisible();
|
|
283
|
+
await expect.soft(applicantPage.ApplicationRecordReviewTotalFPL_Text).toBeVisible();
|
|
284
|
+
await expect.soft(applicantPage.ApplicationRecordTotalHouseholdMonthlyIncome_Text).toBeVisible();
|
|
285
|
+
await expect.soft(applicantPage.ApplicationRecordReviewerTotalHouseholdIncome_Text).toBeVisible();
|
|
286
|
+
// 3. Verify currency values are formatted correctly when present by keeping the summary visible.
|
|
287
|
+
await expect.soft(applicantPage.ApplicationRecordName_Heading).toBeVisible();
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
test('TC-20 - Pending Application Warning Displays @auth', async () => {
|
|
291
|
+
// 1. Open a pending or new application record.
|
|
292
|
+
await applicantPage.openFirstMyApplicationsRecord();
|
|
293
|
+
// 2. Inspect the warning area above the tabs/actions.
|
|
294
|
+
await expect.soft(applicantPage.ApplicationRecordPendingWarning_Text).toBeVisible();
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
test('TC-21 - Record Details Tab Displays Application Fields @auth', async () => {
|
|
298
|
+
// 1. Open an application record.
|
|
299
|
+
await applicantPage.openFirstMyApplicationsRecord();
|
|
300
|
+
// 2. Select DETAILS.
|
|
301
|
+
await applicantPage.selectApplicationRecordDetailsTab();
|
|
302
|
+
// 3-4. Verify the details section and key fields.
|
|
303
|
+
await expect.soft(applicantPage.ApplicationRecordSubmitDate_Text).toBeVisible();
|
|
304
|
+
await expect.soft(applicantPage.ApplicationRecordAssignedDate_Text).toBeVisible();
|
|
305
|
+
await expect.soft(applicantPage.ApplicationRecordName_Heading).toBeVisible();
|
|
306
|
+
await expect.soft(applicantPage.ApplicationRecordOwnerName_Text).toBeVisible();
|
|
307
|
+
await expect.soft(applicantPage.ApplicationRecordStatus_Text).toBeVisible();
|
|
308
|
+
await expect.soft(applicantPage.ApplicationRecordEndDate_Text).toBeVisible();
|
|
309
|
+
await expect.soft(applicantPage.ApplicationRecordHouseholdNumber_Text).toBeVisible();
|
|
310
|
+
await expect.soft(applicantPage.ApplicationRecordHouseholdMembers_Text).toBeVisible();
|
|
311
|
+
await expect.soft(applicantPage.ApplicationRecordSocialServices_Text).toBeVisible();
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
test('TC-22 - Related Tab Displays Related Records Or Empty State @auth', async () => {
|
|
315
|
+
// 1. Open an application record.
|
|
316
|
+
await applicantPage.openFirstMyApplicationsRecord();
|
|
317
|
+
// 2. Select RELATED.
|
|
318
|
+
await applicantPage.selectApplicationRecordRelatedTab();
|
|
319
|
+
// 3. Wait for related content to load.
|
|
320
|
+
await expect.soft(applicantPage.ApplicationRecordRelatedContent_Block).toBeVisible();
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
test('TC-23 - Resume Application Navigates To Application Flow @auth', async ({ page }) => {
|
|
324
|
+
// 1. Open a pending application record.
|
|
325
|
+
await applicantPage.openFirstMyApplicationsRecord();
|
|
326
|
+
// 2. Click RESUME APPLICATION.
|
|
327
|
+
await applicantPage.clickResumeApplicationButton();
|
|
328
|
+
// 3. Wait for navigation or embedded flow content.
|
|
329
|
+
await expect.soft(page).toHaveURL(/createapplication|omniscript|careprogram/i);
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
test('TC-24 - Schedule Appointment Opens And Cancels Safely @auth', async () => {
|
|
333
|
+
// 1. Open an application record.
|
|
334
|
+
await applicantPage.openFirstMyApplicationsRecord();
|
|
335
|
+
// 2. Click Schedule Appointment.
|
|
336
|
+
await applicantPage.clickScheduleAppointmentButton();
|
|
337
|
+
// 3. Verify the appointment UI, modal, or dialog opens.
|
|
338
|
+
await expect.soft(applicantPage.ApplicationRecordDialog_Block).toBeVisible();
|
|
339
|
+
// 4. Cancel or close the dialog.
|
|
340
|
+
await applicantPage.cancelOrCloseOpenDialog();
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
test('TC-25 - Request Appeal Opens And Cancels Safely @auth', async () => {
|
|
344
|
+
// 1. Open an application record.
|
|
345
|
+
await applicantPage.openFirstMyApplicationsRecord();
|
|
346
|
+
// 2. Click Request an Appeal.
|
|
347
|
+
await applicantPage.clickRequestAppealButton();
|
|
348
|
+
// 3. Verify the appeal UI, modal, or dialog opens.
|
|
349
|
+
await expect.soft(applicantPage.ApplicationRecordDialog_Block).toBeVisible();
|
|
350
|
+
// 4. Cancel or close the dialog.
|
|
351
|
+
await applicantPage.cancelOrCloseOpenDialog();
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
test('TC-28 - Logout Protects My Applications Route @auth', async ({ page }) => {
|
|
355
|
+
// 1. Open My Applications with an authenticated session.
|
|
356
|
+
await applicantPage.waitForMyApplicationsPageReady();
|
|
357
|
+
// 2. Click Logout.
|
|
358
|
+
await applicantPage.clickLogoutButton();
|
|
359
|
+
await page.waitForLoadState('networkidle', { timeout: 15_000 }).catch(() => {});
|
|
360
|
+
// 3. Attempt to navigate back to the My Applications URL.
|
|
361
|
+
await applicantPage.gotoURL(MY_APPLICATIONS_PATH, { handleConsentUpdate: false });
|
|
362
|
+
await expect.soft(page).toHaveURL(/\/polkphpapplicant\/s\/login/i);
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
test.fixme('TC-29 - Invalid Or Unauthorized Application Record Access @auth', async () => {
|
|
366
|
+
// Requires a known invalid or cross-applicant CareProgram record ID supplied by test data.
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
test.fixme('TC-30 - Applicant With No Applications @auth', async () => {
|
|
370
|
+
// Requires a seeded applicant account with no application records.
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
test('TC-31 - Large Result Set And Virtual Scrolling @auth', async ({ page }) => {
|
|
374
|
+
// 1. Open My Applications.
|
|
375
|
+
await applicantPage.waitForMyApplicationsPageReady();
|
|
376
|
+
// 2. Verify the summary indicates 50+ items or equivalent.
|
|
377
|
+
await expect.soft(applicantPage.MyApplicationsListSummary_Text).toContainText(/50\+|items/i);
|
|
378
|
+
// 3. Scroll through the list.
|
|
379
|
+
await applicantPage.MyApplicationsTableBody_Block.scrollIntoViewIfNeeded();
|
|
380
|
+
await page.mouse.wheel(0, 1200);
|
|
381
|
+
// 4. Verify rows continue loading and column headers remain usable.
|
|
382
|
+
await expect.soft(applicantPage.MyApplicationsColumnHeaders_Block).toBeVisible();
|
|
383
|
+
await expect.soft(applicantPage.MyApplicationsFirstApplication_Lnk).toBeVisible();
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
test.fixme('TC-32 - Duplicate Application Names @auth', async () => {
|
|
387
|
+
// Requires stable duplicate application record IDs in test data to assert unique navigation.
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
test('TC-33 - Keyboard Navigation @auth @accessibility', async ({ page }) => {
|
|
391
|
+
// 1. Open My Applications.
|
|
392
|
+
await applicantPage.waitForMyApplicationsPageReady();
|
|
393
|
+
// 2. Use Tab and Shift+Tab to move through controls.
|
|
394
|
+
await page.keyboard.press('Tab');
|
|
395
|
+
await page.keyboard.press('Tab');
|
|
396
|
+
await page.keyboard.press('Shift+Tab');
|
|
397
|
+
// 3. Use Enter or Space to activate menus.
|
|
398
|
+
await applicantPage.MyApplicationsSearch_Input.focus();
|
|
399
|
+
await page.keyboard.press('Tab');
|
|
400
|
+
await page.keyboard.press('Enter');
|
|
401
|
+
// 4. Dismiss menus with Escape.
|
|
402
|
+
await page.keyboard.press('Escape');
|
|
403
|
+
await expect.soft(applicantPage.MyApplications_Table).toBeVisible();
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
test('TC-34 - Axe Accessibility Baseline @auth @accessibility', async ({ page }) => {
|
|
407
|
+
// 1. Open My Applications in an authenticated session.
|
|
408
|
+
await applicantPage.waitForMyApplicationsPageReady();
|
|
409
|
+
// 2. Run Axe against the rendered page with WCAG 2.x A/AA tags.
|
|
410
|
+
const accessibilityScanResults = await new AxeBuilder({ page })
|
|
411
|
+
.withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa'])
|
|
412
|
+
.analyze();
|
|
413
|
+
expect(accessibilityScanResults.violations.filter((violation) => ['critical', 'serious'].includes(violation.impact ?? ''))).toEqual([]);
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
test('TC-35 - Accessible Names And Table Semantics @auth @accessibility', async () => {
|
|
417
|
+
// 1. Open My Applications.
|
|
418
|
+
await applicantPage.waitForMyApplicationsPageReady();
|
|
419
|
+
// 2. Verify search input has accessible name Search this list....
|
|
420
|
+
await expect.soft(applicantPage.MyApplicationsSearch_Input).toBeVisible();
|
|
421
|
+
// 3. Verify icon-only buttons expose usable names or titles.
|
|
422
|
+
await expect.soft(applicantPage.MyApplicationsListViewControls_Btn).toBeVisible();
|
|
423
|
+
await expect.soft(applicantPage.MyApplicationsRefresh_Btn).toBeVisible();
|
|
424
|
+
await expect.soft(applicantPage.MyApplicationsCharts_Btn).toBeVisible();
|
|
425
|
+
await expect.soft(applicantPage.MyApplicationsFilters_Btn).toBeVisible();
|
|
426
|
+
await expect.soft(applicantPage.MyApplicationsLastModifiedDateColumnActions_Btn).toBeVisible();
|
|
427
|
+
// 4. Verify sortable column headers communicate sort state after sorting.
|
|
428
|
+
await applicantPage.clickMyApplicationsLastModifiedDateSort();
|
|
429
|
+
expect.soft(await applicantPage.getMyApplicationsSortStateText()).toMatch(/ascending|descending|sorted|Last Modified Date/i);
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
test('TC-36 - Responsive Layout @auth', async ({ page }) => {
|
|
433
|
+
for (const viewport of [
|
|
434
|
+
{ width: 1500, height: 730 },
|
|
435
|
+
{ width: 768, height: 1024 },
|
|
436
|
+
{ width: 412, height: 732 },
|
|
437
|
+
]) {
|
|
438
|
+
// 1-2. Open My Applications at desktop, tablet, and mobile viewport sizes.
|
|
439
|
+
await page.setViewportSize(viewport);
|
|
440
|
+
await applicantPage.gotoMyApplicationsPage();
|
|
441
|
+
// 3. Verify header navigation, search, list controls, table, row actions, and footer.
|
|
442
|
+
await expect.soft(applicantPage.Logout_Btn).toBeVisible();
|
|
443
|
+
await expect.soft(applicantPage.MyApplicationsSearch_Input).toBeVisible();
|
|
444
|
+
await expect.soft(applicantPage.MyApplicationsListViewControls_Btn).toBeVisible();
|
|
445
|
+
await expect.soft(applicantPage.MyApplications_Table).toBeVisible();
|
|
446
|
+
await expect.soft(applicantPage.MyApplicationsFirstRowActions_Btn).toBeVisible();
|
|
447
|
+
// 4. Open a record detail page at each viewport.
|
|
448
|
+
await applicantPage.openFirstMyApplicationsRecord();
|
|
449
|
+
await expect.soft(applicantPage.ApplicationRecordName_Heading).toBeVisible();
|
|
450
|
+
}
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
test('TC-37 - Browser Console And Network Health @auth', async ({ page }) => {
|
|
454
|
+
const consoleErrors: string[] = [];
|
|
455
|
+
const failedRequests: string[] = [];
|
|
456
|
+
|
|
457
|
+
page.on('console', (message) => {
|
|
458
|
+
if (message.type() === 'error') {
|
|
459
|
+
consoleErrors.push(message.text());
|
|
460
|
+
}
|
|
461
|
+
});
|
|
462
|
+
page.on('requestfailed', (request) => {
|
|
463
|
+
if (['document', 'xhr', 'fetch'].includes(request.resourceType())) {
|
|
464
|
+
failedRequests.push(`${request.resourceType()} ${request.url()} ${request.failure()?.errorText ?? ''}`);
|
|
465
|
+
}
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
// 1. Open My Applications.
|
|
469
|
+
await applicantPage.gotoMyApplicationsPage();
|
|
470
|
+
// 2. Monitor during search, sort, and record navigation.
|
|
471
|
+
await applicantPage.searchMyApplicationsList('John');
|
|
472
|
+
await applicantPage.clickMyApplicationsLastModifiedDateSort();
|
|
473
|
+
await applicantPage.openFirstMyApplicationsRecord();
|
|
474
|
+
|
|
475
|
+
expect.soft(failedRequests).toEqual([]);
|
|
476
|
+
expect.soft(consoleErrors.filter((error) => !/ResizeObserver|Aura|Lightning/i.test(error))).toEqual([]);
|
|
477
|
+
});
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
test.describe('Unauthenticated My Applications Access', () => {
|
|
481
|
+
test.use({ storageState: { cookies: [], origins: [] } });
|
|
482
|
+
|
|
483
|
+
let applicantPage: ApplicantPage;
|
|
484
|
+
|
|
485
|
+
test.beforeEach(async ({ page }) => {
|
|
486
|
+
applicantPage = new ApplicantPage(page);
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
test('TC-26 - Direct Unauthenticated Access Redirects To Login @no-auth ', async ({ page }) => {
|
|
490
|
+
// 1. Clear browser storage or create a new context without storage state.
|
|
491
|
+
await page.context().clearCookies();
|
|
492
|
+
// 2. Navigate directly to /polkphpapplicant/s/careprogram/CareProgram/Default.
|
|
493
|
+
await applicantPage.gotoURL(MY_APPLICATIONS_PATH, { handleConsentUpdate: false });
|
|
494
|
+
// Verify redirect and login form.
|
|
495
|
+
await expect.soft(page).toHaveURL(/\/polkphpapplicant\/s\/login/i);
|
|
496
|
+
expect.soft(page.url()).toContain('startURL');
|
|
497
|
+
await expect.soft(applicantPage.UsernameField_Input).toBeVisible();
|
|
498
|
+
await expect.soft(applicantPage.PasswordField_Input).toBeVisible();
|
|
499
|
+
await expect.soft(applicantPage.LoginSubmit_Btn).toBeVisible();
|
|
500
|
+
await expect.soft(applicantPage.ForgotPassword_Lnk).toBeVisible();
|
|
501
|
+
await expect.soft(applicantPage.SignUp_Lnk).toBeVisible();
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
test('TC-27 - Login Redirect Returns User To My Applications @no-auth', async ({ page }) => {
|
|
505
|
+
test.skip(!testConfig.login || !testConfig.password, 'LOGIN and PASSWORD must be set for redirect login coverage.');
|
|
506
|
+
// 1. Navigate directly to My Applications without auth.
|
|
507
|
+
await applicantPage.gotoURL(MY_APPLICATIONS_PATH, { handleConsentUpdate: false });
|
|
508
|
+
await expect.soft(page).toHaveURL(/\/polkphpapplicant\/s\/login/i);
|
|
509
|
+
// 2. Complete login with valid credentials.
|
|
510
|
+
await applicantPage.loginAsApplicant(testConfig.login, testConfig.password);
|
|
511
|
+
// 3. Wait for post-login navigation.
|
|
512
|
+
await applicantPage.waitForMyApplicationsPageReady();
|
|
513
|
+
expect.soft(page.url()).toContain('/polkphpapplicant/s/careprogram/CareProgram/Default');
|
|
514
|
+
});
|
|
515
|
+
});
|
|
516
|
+
});
|