awaitpage.locator('#user_email').fill('admin@example.test');awaitpage.locator('#user_password').fill('Admin test password 123!');awaitPromise.all([page.waitForNavigation({waitUntil:'load'}),page.locator('#login button[type="submit"]').click()]);awaitrequire('./browser_mfa')(page,'Admin test password 123!');awaitpage.waitForFunction(()=>typeofformSubmit==='function');
awaitpage.getByText('Browser',{exact:true}).waitFor();pass('Administrative account form saves through asynchronous validation');
assert((awaitpage.locator('.toast-body').innerText()).includes('invitation'));pass('Failed invitation is visible after returning to the user list');
constrow=page.locator('tr.user-row').filter({hasText:'browser_tester'});awaitrow.click();awaitpage.locator('.userSummary').waitFor();pass('Clicking a user opens the repaired summary');
awaitpage.route('**/*',failUsage);awaitpage.locator('.btnDelete').click();awaitpage.getByText('Deliberate usage-check failure',{exact:true}).waitFor();assert(!awaitpage.locator('#confirm-dialog').isVisible());pass('A failed deletion check shows an error and does not open confirmation');awaitpage.unroute('**/*',failUsage);
awaitpage.locator('.btnDelete').click();awaitpage.locator('#confirm-dialog').waitFor({state:'visible'});awaitpage.locator('#confirm-dialog .btnConfirmDialogConfirm').click();awaitpage.locator('.setupUsers').waitFor();assert(awaitpage.locator('tr.user-row').filter({hasText:'browser_tester'}).count()===0);pass('Deleting an unused account succeeds through the browser');
awaitsubmit({action:'Dropdowns.setupDropdowntypes'});awaitpage.locator('.setupDropdowntypes').waitFor();awaitpage.locator('.btnAdd').click();awaitpage.locator('#addDropdowntype').waitFor();awaitpage.locator('#dropdowntype_name').fill('browser_type');awaitpage.locator('#dropdowntype_title').fill('Browser type');awaitpage.locator('.btnSave').click();awaitpage.locator('.setupDropdowntypes').waitFor();pass('Dropdown type form works with restricted usage metadata');
awaitpage.getByText('The text editor could not load. Reload this page before saving.',{exact:true}).waitFor();assert(awaitpage.locator('.btnSave').isDisabled());pass('Editor load failure is visible and saving stays disabled');awaitpage.unroute('**/js/suneditor-3.3.3.min.js');
letform=page.locator('form').filter({has:page.getByRole('button',{name:'Create club',exact:true})});awaitform.getByLabel('Club name',{exact:true}).fill('Browser Club');awaitform.getByRole('button',{name:'Create club',exact:true}).click();awaitpage.getByRole('heading',{name:'Browser Club',exact:true}).waitFor();pass('Club creation works in the browser');
assert.equal(awaitpage.locator('.clubSummary input:visible,.clubSummary select:visible').count(),0);pass('Club summary hides all editing fields until an action is opened');
awaitopenClubModal('club');awaitpage.locator('.club-action-modal.show').getByLabel('Club name',{exact:true}).fill('Unsaved name');awaitpage.locator('.club-action-modal.show').getByRole('button',{name:'Cancel',exact:true}).click();awaitpage.locator('.club-action-modal.show').waitFor({state:'hidden'});assert.equal(awaitpage.locator('.clubSummary h1').textContent(),'Browser Club');pass('Canceling a club edit leaves the club unchanged');
awaitopenClubModal('club');awaitpage.locator('.club-action-modal.show').getByLabel('Club name',{exact:true}).fill('Browser Club');awaitPromise.all([page.waitForNavigation(),page.locator('.club-action-modal.show').getByRole('button',{name:'Save club',exact:true}).click()]);assert.equal(awaitpage.locator('.clubSummary h1').textContent(),'Browser Club');pass('Club edits save through their modal and return to the summary');
awaitopenClubModal('member');form=page.locator('form').filter({has:page.getByRole('button',{name:'Add member',exact:true})});awaitform.getByLabel('Username or email').fill('testadmin');awaitform.getByRole('button',{name:'Add member',exact:true}).click();awaitpage.getByRole('cell',{name:'Test Administrator',exact:true}).waitFor();pass('Membership assignment works in the browser');
awaitopenClubModal('season');form=page.locator('form').filter({has:page.getByRole('button',{name:'Add season',exact:true})});awaitform.getByLabel('Season name').fill('Browser Season');awaitform.getByLabel('Starts on').fill('2026-01-01');awaitform.getByLabel('Ends on').fill('2025-12-31');awaitform.getByRole('button',{name:'Add season',exact:true}).click();awaitpage.locator('.club-action-modal.show .club-modal-error:not([hidden])').waitFor();assert.equal(awaitform.getByLabel('Season name').inputValue(),'Browser Season');pass('Server validation stays inside the modal and preserves entered values');awaitform.getByLabel('Ends on').fill('2026-12-31');awaitform.getByRole('button',{name:'Add season',exact:true}).click();awaitpage.getByText('Browser Season (2026-01-01 to 2026-12-31)',{exact:true}).waitFor();pass('Season creation works in the browser');
awaitopenClubModal('charge');form=page.locator('form').filter({has:page.getByRole('button',{name:'Save dues',exact:true})});awaitform.getByLabel('Dues (USD)').fill('10.00');awaitform.getByRole('button',{name:'Save dues',exact:true}).click();awaitpage.getByRole('button',{name:'Record payment',exact:true}).waitFor();pass('Dues entry works in the browser');
awaitopenClubModal('payment');form=page.locator('form').filter({has:page.getByRole('button',{name:'Record payment',exact:true})});awaitform.getByLabel('Payment (USD)').fill('5.00');awaitform.getByRole('button',{name:'Record payment',exact:true}).click();awaitpage.getByRole('cell',{name:'$5.00',exact:true}).first().waitFor();pass('Recording a payment updates the displayed balance');
form=page.locator('form').filter({has:page.getByRole('button',{name:'Save correction',exact:true})});awaitform.getByLabel('Corrected total (USD)').fill('8.00');awaitform.getByLabel('Reason',{exact:true}).fill('Correct season rate');awaitform.getByRole('button',{name:'Save correction',exact:true}).click();awaitpage.getByRole('cell',{name:'$8.00',exact:true}).waitFor();pass('Dues correction updates the balance and preserves history in the browser');
form=page.locator('form').filter({has:page.getByRole('button',{name:'Confirm reversal',exact:true})});awaitform.getByLabel('Reversal reason').fill('Wrong receipt');awaitform.getByRole('button',{name:'Confirm reversal',exact:true}).click();awaitpage.getByText('Reversed: Wrong receipt',{exact:false}).waitFor();pass('Payment reversal is recorded visibly without deleting the original payment');
awaitopenClubModal('invite');awaitpage.locator('.club-action-modal.show').getByLabel('Invitation email',{exact:true}).fill('revocation@example.test');awaitPromise.all([page.waitForNavigation(),page.locator('.club-action-modal.show').getByRole('button',{name:'Send club invitation',exact:true}).click()]);constinvitation=page.locator('li').filter({hasText:'revocation@example.test'});awaitinvitation.locator('[data-club-step=revokeInvitation]').click();awaitpage.locator('.club-action-modal.show').getByRole('button',{name:'Cancel',exact:true}).click();awaitpage.locator('.club-action-modal.show').waitFor({state:'hidden'});assert((awaitinvitation.textContent()).includes('pending'));awaitinvitation.locator('[data-club-step=revokeInvitation]').click();awaitPromise.all([page.waitForNavigation(),page.locator('.club-action-modal.show').getByRole('button',{name:'Revoke invitation',exact:true}).click()]);assert((awaitpage.locator('li').filter({hasText:'revocation@example.test'}).textContent()).includes('revoked'));pass('Invitation revocation requires modal confirmation and Cancel preserves the invitation');
constledgerDownload=page.waitForEvent('download');awaitpage.getByRole('button',{name:'Export financial history (CSV)',exact:true}).click();assert.equal((awaitledgerDownload).suggestedFilename(),'club-financial-history.csv');pass('Financial history can be downloaded from the browser');
awaitsubmit({action:'Reports.selectReport'});awaitpage.getByRole('heading',{name:'Roster reports'}).waitFor();constdownloadEvent=page.waitForEvent('download');awaitpage.getByRole('button',{name:'Download CSV'}).click();constdownload=awaitdownloadEvent;assert.equal(download.suggestedFilename(),'club-roster.csv');pass('Report catalog downloads the roster CSV in the browser');
awaitpage.goto(process.env.HCR_TEST_URL+'/main.php?Xaction=verifyemail');awaitpage.getByRole('heading',{name:'Verify your email'}).waitFor();awaitpage.getByLabel('Email',{exact:true}).fill('unknown@example.test');awaitpage.getByRole('button',{name:'Send verification link'}).click();awaitpage.getByText('If an active account needs verification',{exact:false}).waitFor();pass('Verification request page gives a neutral response in the browser');