feat(accessibility): enhance AccountViewView and document test suite
- Add ARIA annotations and roles to AccountViewView for better screen reader support - Add role="tooltip" to API server description - Improve input control accessibility with proper ARIA attributes - Add descriptive labels and aria-labels for interactive elements - Create comprehensive README.md for Playwright test suite - Document test structure and organization - Add setup instructions and prerequisites - Include troubleshooting guide and contribution guidelines - Link to related documentation This change improves accessibility compliance and makes the test suite more maintainable for contributors.
This commit is contained in:
2960
package-lock.json
generated
2960
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -3,7 +3,7 @@
|
||||
<TopMessage />
|
||||
|
||||
<!-- CONTENT -->
|
||||
<section id="Content" class="p-6 pb-24 max-w-3xl mx-auto">
|
||||
<main id="Content" class="p-6 pb-24 max-w-3xl mx-auto" role="main" aria-label="Account Profile">
|
||||
<!-- Heading -->
|
||||
<h1 id="ViewHeading" class="text-4xl text-center font-light">
|
||||
Your Identity
|
||||
@@ -14,6 +14,8 @@
|
||||
v-if="!activeDid"
|
||||
id="noticeBeforeShare"
|
||||
class="bg-amber-200 text-amber-900 border-amber-500 border-dashed border text-center rounded-md overflow-hidden px-4 py-3 mt-4"
|
||||
role="alert"
|
||||
aria-live="polite"
|
||||
>
|
||||
<p class="mb-4">
|
||||
<b>Note:</b> Before you can share with others or take any action, you
|
||||
@@ -28,10 +30,12 @@
|
||||
</div>
|
||||
|
||||
<!-- Identity Details -->
|
||||
<div
|
||||
<section
|
||||
id="sectionIdentityDetails"
|
||||
class="bg-slate-100 rounded-md overflow-hidden px-4 py-3 mt-4"
|
||||
aria-labelledby="identityDetailsHeading"
|
||||
>
|
||||
<h2 id="identityDetailsHeading" class="sr-only">Identity Details</h2>
|
||||
<div v-if="givenName">
|
||||
<h2 class="text-xl font-semibold mb-2">
|
||||
<span class="whitespace-nowrap">
|
||||
@@ -75,11 +79,17 @@
|
||||
:profile-image-url="profileImageUrl"
|
||||
class="inline-block align-text-bottom border border-slate-300 rounded"
|
||||
@click="showLargeIdenticonUrl = profileImageUrl"
|
||||
role="button"
|
||||
aria-label="View profile image in large size"
|
||||
tabindex="0"
|
||||
/>
|
||||
<font-awesome
|
||||
icon="trash-can"
|
||||
class="text-red-500 fa-fw ml-8 mt-8 w-12 h-12"
|
||||
@click="confirmDeleteImage"
|
||||
role="button"
|
||||
aria-label="Delete profile image"
|
||||
tabindex="0"
|
||||
/>
|
||||
</span>
|
||||
<div v-else class="text-center">
|
||||
@@ -140,17 +150,20 @@
|
||||
<div
|
||||
class="text-sm text-slate-500 flex justify-start items-center mb-1"
|
||||
data-testId="didWrapper"
|
||||
role="region"
|
||||
aria-label="Your Identifier"
|
||||
>
|
||||
<code class="truncate">{{ activeDid }}</code>
|
||||
<code class="truncate" aria-label="Your DID">{{ activeDid }}</code>
|
||||
<button
|
||||
class="ml-2"
|
||||
@click="
|
||||
doCopyTwoSecRedo(activeDid, () => (showDidCopy = !showDidCopy))
|
||||
"
|
||||
aria-label="Copy DID to clipboard"
|
||||
>
|
||||
<font-awesome icon="copy" class="text-slate-400 fa-fw"></font-awesome>
|
||||
<font-awesome icon="copy" class="text-slate-400 fa-fw" aria-hidden="true"></font-awesome>
|
||||
</button>
|
||||
<span v-show="showDidCopy">Copied</span>
|
||||
<span v-show="showDidCopy" role="status" aria-live="polite">Copied</span>
|
||||
</div>
|
||||
|
||||
<div class="text-blue-500 text-sm font-bold">
|
||||
@@ -158,7 +171,7 @@
|
||||
Your Activity
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Registration notice -->
|
||||
<!--
|
||||
@@ -169,6 +182,8 @@
|
||||
v-if="!isRegistered"
|
||||
id="noticeBeforeAnnounce"
|
||||
class="bg-amber-200 text-amber-900 border-amber-500 border-dashed border text-center rounded-md overflow-hidden px-4 py-3 mt-4"
|
||||
role="alert"
|
||||
aria-live="polite"
|
||||
>
|
||||
<p class="mb-4">
|
||||
<b>Note:</b> Before you can publicly announce a new project or time
|
||||
@@ -182,27 +197,31 @@
|
||||
</router-link>
|
||||
</div>
|
||||
|
||||
<div
|
||||
<section
|
||||
v-if="isRegistered"
|
||||
id="sectionNotifications"
|
||||
class="bg-slate-100 rounded-md overflow-hidden px-4 py-4 mt-8 mb-8"
|
||||
aria-labelledby="notificationsHeading"
|
||||
>
|
||||
<!-- label -->
|
||||
<div class="mb-2 font-bold">Notifications</div>
|
||||
<h2 id="notificationsHeading" class="mb-2 font-bold">Notifications</h2>
|
||||
<div class="flex items-center justify-between">
|
||||
<!-- label -->
|
||||
<div>
|
||||
Reminder Notification
|
||||
<font-awesome
|
||||
icon="question-circle"
|
||||
<button
|
||||
class="text-slate-400 fa-fw ml-2 cursor-pointer"
|
||||
@click.stop="showReminderNotificationInfo"
|
||||
/>
|
||||
aria-label="Learn more about reminder notifications"
|
||||
>
|
||||
<font-awesome icon="question-circle" aria-hidden="true"></font-awesome>
|
||||
</button>
|
||||
</div>
|
||||
<!-- toggle -->
|
||||
<div
|
||||
class="relative ml-2 cursor-pointer"
|
||||
@click="showReminderNotificationChoice()"
|
||||
role="switch"
|
||||
:aria-checked="notifyingReminder"
|
||||
aria-label="Toggle reminder notifications"
|
||||
tabindex="0"
|
||||
>
|
||||
<!-- input -->
|
||||
<input v-model="notifyingReminder" type="checkbox" class="sr-only" />
|
||||
@@ -253,49 +272,47 @@
|
||||
<router-link class="pl-4 text-sm text-blue-500" to="/help-notifications">
|
||||
Troubleshoot your notifications.
|
||||
</router-link>
|
||||
</div>
|
||||
</section>
|
||||
<PushNotificationPermission ref="pushNotificationPermission" />
|
||||
|
||||
<div
|
||||
<section
|
||||
id="sectionSearchLocation"
|
||||
class="flex justify-between bg-slate-100 rounded-md overflow-hidden px-4 py-4 mt-8 mb-8"
|
||||
aria-labelledby="searchLocationHeading"
|
||||
>
|
||||
<!-- label -->
|
||||
<span class="mb-2 font-bold">Location for Searches</span>
|
||||
<h2 id="searchLocationHeading" class="mb-2 font-bold">Location for Searches</h2>
|
||||
<router-link
|
||||
:to="{ name: 'search-area' }"
|
||||
class="text-m bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-4 py-2 rounded-md mb-2"
|
||||
>
|
||||
{{ isSearchAreasSet ? "Change" : "Set" }} Search Area…
|
||||
</router-link>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- User Profile -->
|
||||
<div
|
||||
<section
|
||||
v-if="isRegistered"
|
||||
class="bg-slate-100 rounded-md overflow-hidden px-4 py-4 mt-8 mb-8"
|
||||
aria-labelledby="userProfileHeading"
|
||||
>
|
||||
<div v-if="loadingProfile" class="text-center mb-2">
|
||||
<font-awesome
|
||||
icon="spinner"
|
||||
class="fa-spin text-slate-400"
|
||||
></font-awesome>
|
||||
Loading profile...
|
||||
</div>
|
||||
<div v-else class="flex items-center mb-2">
|
||||
<h2 id="userProfileHeading" class="flex items-center mb-2">
|
||||
<span class="font-bold">Public Profile</span>
|
||||
<font-awesome
|
||||
icon="circle-info"
|
||||
<button
|
||||
class="text-slate-400 fa-fw ml-2 cursor-pointer"
|
||||
@click="showProfileInfo"
|
||||
/>
|
||||
</div>
|
||||
aria-label="Learn more about public profile"
|
||||
>
|
||||
<font-awesome icon="circle-info" aria-hidden="true"></font-awesome>
|
||||
</button>
|
||||
</h2>
|
||||
<textarea
|
||||
v-model="userProfileDesc"
|
||||
class="w-full h-32 p-2 border border-slate-300 rounded-md"
|
||||
placeholder="Write something about yourself for the public..."
|
||||
:readonly="loadingProfile || savingProfile"
|
||||
:class="{ 'bg-slate-100': loadingProfile || savingProfile }"
|
||||
aria-label="Public profile description"
|
||||
:aria-busy="loadingProfile || savingProfile"
|
||||
></textarea>
|
||||
|
||||
<div class="flex items-center mb-4" @click="toggleUserProfileLocation">
|
||||
@@ -364,18 +381,19 @@
|
||||
</div>
|
||||
<div v-else-if="loadingProfile">Loading...</div>
|
||||
<div v-else>Saving...</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div
|
||||
<section
|
||||
v-if="activeDid"
|
||||
id="sectionUsageLimits"
|
||||
class="bg-slate-100 rounded-md overflow-hidden px-4 py-4 mt-8 mb-8"
|
||||
aria-labelledby="usageLimitsHeading"
|
||||
>
|
||||
<div class="mb-2 font-bold">Usage Limits</div>
|
||||
<h2 id="usageLimitsHeading" class="mb-2 font-bold">Usage Limits</h2>
|
||||
<!-- show spinner if loading limits -->
|
||||
<div v-if="loadingLimits" class="text-center">
|
||||
<div v-if="loadingLimits" class="text-center" role="status" aria-live="polite">
|
||||
Checking…
|
||||
<font-awesome icon="spinner" class="fa-spin"></font-awesome>
|
||||
<font-awesome icon="spinner" class="fa-spin" aria-hidden="true"></font-awesome>
|
||||
</div>
|
||||
<div class="mb-4 text-center">
|
||||
{{ limitsMessage }}
|
||||
@@ -423,7 +441,7 @@
|
||||
>
|
||||
Recheck Limits
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<DataExportSection :active-did="activeDid" />
|
||||
|
||||
@@ -435,7 +453,8 @@
|
||||
>
|
||||
Advanced
|
||||
</h3>
|
||||
<div v-if="showAdvanced || showGeneralAdvanced" id="sectionAdvanced">
|
||||
<section v-if="showAdvanced || showGeneralAdvanced" id="sectionAdvanced" aria-labelledby="advancedHeading">
|
||||
<h2 id="advancedHeading" class="text-blue-500 text-sm font-semibold mb-3">Advanced</h2>
|
||||
<p class="text-rose-600 mb-8">
|
||||
Beware: the features here can be confusing and even change data in ways
|
||||
you do not expect. But we support your freedom!
|
||||
@@ -606,41 +625,60 @@
|
||||
|
||||
<div id="sectionClaimServer">
|
||||
<h2 class="text-slate-500 text-sm font-bold mt-4">Claim Server</h2>
|
||||
<div class="px-4 py-4">
|
||||
<div class="px-4 py-4" role="group" aria-labelledby="claimServerHeading">
|
||||
<h3 id="claimServerHeading" class="sr-only">Claim Server Configuration</h3>
|
||||
<label for="apiServerInput" class="sr-only">API Server URL</label>
|
||||
<input
|
||||
id="apiServerInput"
|
||||
v-model="apiServerInput"
|
||||
type="text"
|
||||
class="block w-full rounded border border-slate-400 px-4 py-2"
|
||||
aria-describedby="apiServerDescription"
|
||||
placeholder="Enter API server URL"
|
||||
/>
|
||||
<div
|
||||
id="apiServerDescription"
|
||||
class="sr-only"
|
||||
role="tooltip"
|
||||
>
|
||||
Enter the URL for the claim server. You can use the buttons below to quickly set common server URLs.
|
||||
</div>
|
||||
<button
|
||||
v-if="apiServerInput != apiServer"
|
||||
class="w-full px-4 rounded bg-yellow-500 border border-slate-400"
|
||||
@click="onClickSaveApiServer()"
|
||||
aria-label="Save API server URL"
|
||||
>
|
||||
<font-awesome
|
||||
icon="floppy-disk"
|
||||
class="fa-fw"
|
||||
color="white"
|
||||
aria-hidden="true"
|
||||
></font-awesome>
|
||||
</button>
|
||||
<button
|
||||
class="px-3 rounded bg-slate-200 border border-slate-400"
|
||||
@click="apiServerInput = AppConstants.PROD_ENDORSER_API_SERVER"
|
||||
>
|
||||
Use Prod
|
||||
</button>
|
||||
<button
|
||||
class="px-3 rounded bg-slate-200 border border-slate-400"
|
||||
@click="apiServerInput = AppConstants.TEST_ENDORSER_API_SERVER"
|
||||
>
|
||||
Use Test
|
||||
</button>
|
||||
<button
|
||||
class="px-3 rounded bg-slate-200 border border-slate-400"
|
||||
@click="apiServerInput = AppConstants.LOCAL_ENDORSER_API_SERVER"
|
||||
>
|
||||
Use Local
|
||||
</button>
|
||||
<div class="mt-2" role="group" aria-label="Quick server selection">
|
||||
<button
|
||||
class="px-3 rounded bg-slate-200 border border-slate-400"
|
||||
@click="apiServerInput = AppConstants.PROD_ENDORSER_API_SERVER"
|
||||
aria-label="Use production server URL"
|
||||
>
|
||||
Use Prod
|
||||
</button>
|
||||
<button
|
||||
class="px-3 rounded bg-slate-200 border border-slate-400"
|
||||
@click="apiServerInput = AppConstants.TEST_ENDORSER_API_SERVER"
|
||||
aria-label="Use test server URL"
|
||||
>
|
||||
Use Test
|
||||
</button>
|
||||
<button
|
||||
class="px-3 rounded bg-slate-200 border border-slate-400"
|
||||
@click="apiServerInput = AppConstants.LOCAL_ENDORSER_API_SERVER"
|
||||
aria-label="Use local server URL"
|
||||
>
|
||||
Use Local
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<label
|
||||
@@ -878,8 +916,8 @@
|
||||
>
|
||||
View Logs
|
||||
</router-link>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
|
||||
@@ -179,7 +179,7 @@ test('Confirm test API setting (may fail if you are running your own Time Safari
|
||||
const endorserTermInConfig = endorserTerm?.substring(ENDORSER_ENV_NAME.length + 1);
|
||||
|
||||
const endorserServer = endorserTermInConfig || 'https://test-api.endorser.ch';
|
||||
await expect(page.getByRole('textbox').nth(1)).toHaveValue(endorserServer);
|
||||
await expect(page.locator('#apiServerInput')).toHaveValue(endorserServer);
|
||||
});
|
||||
|
||||
test('Check User 0 can register a random person', async ({ page }) => {
|
||||
|
||||
119
test-playwright/README.md
Normal file
119
test-playwright/README.md
Normal file
@@ -0,0 +1,119 @@
|
||||
# Playwright Test Suite
|
||||
|
||||
This directory contains the automated end-to-end test suite for Time Safari using Playwright. The tests verify critical functionality across web and mobile platforms.
|
||||
|
||||
## Test Structure
|
||||
|
||||
Tests are organized by feature area and numbered for execution order:
|
||||
|
||||
- `00-noid-tests.spec.ts` - Tests for unregistered users
|
||||
- `05-invite.spec.ts` - Contact invitation functionality
|
||||
- `10-check-usage-limits.spec.ts` - Usage limit verification
|
||||
- `20-create-project.spec.ts` - Project creation
|
||||
- `25-create-project-x10.spec.ts` - Bulk project creation
|
||||
- `30-record-gift.spec.ts` - Gift recording
|
||||
- `33-record-gift-x10.spec.ts` - Bulk gift recording
|
||||
- `35-record-gift-from-image-share.spec.ts` - Gift recording from shared images
|
||||
- `37-record-gift-on-project.spec.ts` - Project-specific gift recording
|
||||
- `40-add-contact.spec.ts` - Contact management
|
||||
- `50-record-offer.spec.ts` - Offer recording
|
||||
- `60-new-activity.spec.ts` - Activity feed updates
|
||||
|
||||
## Key Files
|
||||
|
||||
- `testUtils.ts` - Shared test utilities and helper functions
|
||||
- `TESTING.md` - Detailed testing guide and manual test procedures
|
||||
- `playwright.config-local.ts` - Playwright configuration for local testing
|
||||
- `exported-data.json` - Test data for import/export testing
|
||||
|
||||
## Prerequisites
|
||||
|
||||
1. Endorser server running locally (see TESTING.md for setup)
|
||||
2. Playwright browsers installed:
|
||||
```bash
|
||||
npx playwright install
|
||||
```
|
||||
3. For mobile testing:
|
||||
- XCode (for iOS)
|
||||
- Android Studio or connected Android device
|
||||
|
||||
## Running Tests
|
||||
|
||||
### Full Test Suite
|
||||
|
||||
```bash
|
||||
# Run all tests (web + mobile)
|
||||
npm run test:all
|
||||
|
||||
# Run web-only tests
|
||||
npm run test:web
|
||||
```
|
||||
|
||||
### Individual Tests
|
||||
|
||||
```bash
|
||||
# Run a specific test with tracing
|
||||
npx playwright test -c playwright.config-local.ts --trace on test-playwright/40-add-contact.spec.ts
|
||||
```
|
||||
|
||||
### Test Environment Options
|
||||
|
||||
1. Local Endorser Server (default):
|
||||
```bash
|
||||
NODE_ENV=test-local npm run dev
|
||||
```
|
||||
|
||||
2. Global Test Server:
|
||||
```bash
|
||||
VITE_DEFAULT_ENDORSER_API_SERVER=https://test-ledger.time.com npm run dev
|
||||
```
|
||||
|
||||
3. Minimal Test Data:
|
||||
```bash
|
||||
rm ../endorser-ch-test-local.sqlite3
|
||||
NODE_ENV=test-local npm run flyway migrate
|
||||
NODE_ENV=test-local npm run test test/controller0
|
||||
NODE_ENV=test-local npm run dev
|
||||
```
|
||||
|
||||
## Test Data
|
||||
|
||||
The test suite uses predefined test users, with User #0 having registration privileges:
|
||||
- DID: `did:ethr:0x0000694B58C2cC69658993A90D3840C560f2F51F`
|
||||
- Seed phrase available in TESTING.md
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
Common issues and solutions:
|
||||
|
||||
1. **Test Failures**
|
||||
- Some tests may fail intermittently - try rerunning
|
||||
- Check Endorser server logs for backend issues
|
||||
- Verify test environment setup
|
||||
|
||||
2. **Mobile Testing**
|
||||
- Ensure XCode/Android Studio is running
|
||||
- Check device connectivity
|
||||
- Verify browser installation
|
||||
|
||||
3. **Data Issues**
|
||||
- Clear browser data if tests fail due to stale state
|
||||
- Reset IndexedDB if needed
|
||||
- Check service worker status
|
||||
|
||||
For more detailed troubleshooting, see TESTING.md.
|
||||
|
||||
## Contributing
|
||||
|
||||
When adding new tests:
|
||||
1. Follow the existing naming convention
|
||||
2. Use testUtils.ts for common operations
|
||||
3. Add appropriate comments and documentation
|
||||
4. Update this README if adding new test categories
|
||||
5. Consider both web and mobile platforms
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [TESTING.md](./TESTING.md) - Detailed testing guide
|
||||
- [Playwright Documentation](https://playwright.dev/docs/intro)
|
||||
- Endorser server documentation for test setup
|
||||
Reference in New Issue
Block a user