forked from trent_larson/crowd-funder-for-time-pwa
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 />
|
<TopMessage />
|
||||||
|
|
||||||
<!-- CONTENT -->
|
<!-- 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 -->
|
<!-- Heading -->
|
||||||
<h1 id="ViewHeading" class="text-4xl text-center font-light">
|
<h1 id="ViewHeading" class="text-4xl text-center font-light">
|
||||||
Your Identity
|
Your Identity
|
||||||
@@ -14,6 +14,8 @@
|
|||||||
v-if="!activeDid"
|
v-if="!activeDid"
|
||||||
id="noticeBeforeShare"
|
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"
|
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">
|
<p class="mb-4">
|
||||||
<b>Note:</b> Before you can share with others or take any action, you
|
<b>Note:</b> Before you can share with others or take any action, you
|
||||||
@@ -28,10 +30,12 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Identity Details -->
|
<!-- Identity Details -->
|
||||||
<div
|
<section
|
||||||
id="sectionIdentityDetails"
|
id="sectionIdentityDetails"
|
||||||
class="bg-slate-100 rounded-md overflow-hidden px-4 py-3 mt-4"
|
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">
|
<div v-if="givenName">
|
||||||
<h2 class="text-xl font-semibold mb-2">
|
<h2 class="text-xl font-semibold mb-2">
|
||||||
<span class="whitespace-nowrap">
|
<span class="whitespace-nowrap">
|
||||||
@@ -75,11 +79,17 @@
|
|||||||
:profile-image-url="profileImageUrl"
|
:profile-image-url="profileImageUrl"
|
||||||
class="inline-block align-text-bottom border border-slate-300 rounded"
|
class="inline-block align-text-bottom border border-slate-300 rounded"
|
||||||
@click="showLargeIdenticonUrl = profileImageUrl"
|
@click="showLargeIdenticonUrl = profileImageUrl"
|
||||||
|
role="button"
|
||||||
|
aria-label="View profile image in large size"
|
||||||
|
tabindex="0"
|
||||||
/>
|
/>
|
||||||
<font-awesome
|
<font-awesome
|
||||||
icon="trash-can"
|
icon="trash-can"
|
||||||
class="text-red-500 fa-fw ml-8 mt-8 w-12 h-12"
|
class="text-red-500 fa-fw ml-8 mt-8 w-12 h-12"
|
||||||
@click="confirmDeleteImage"
|
@click="confirmDeleteImage"
|
||||||
|
role="button"
|
||||||
|
aria-label="Delete profile image"
|
||||||
|
tabindex="0"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<div v-else class="text-center">
|
<div v-else class="text-center">
|
||||||
@@ -140,17 +150,20 @@
|
|||||||
<div
|
<div
|
||||||
class="text-sm text-slate-500 flex justify-start items-center mb-1"
|
class="text-sm text-slate-500 flex justify-start items-center mb-1"
|
||||||
data-testId="didWrapper"
|
data-testId="didWrapper"
|
||||||
|
role="region"
|
||||||
|
aria-label="Your Identifier"
|
||||||
>
|
>
|
||||||
<code class="truncate">{{ activeDid }}</code>
|
<code class="truncate" aria-label="Your DID">{{ activeDid }}</code>
|
||||||
<button
|
<button
|
||||||
class="ml-2"
|
class="ml-2"
|
||||||
@click="
|
@click="
|
||||||
doCopyTwoSecRedo(activeDid, () => (showDidCopy = !showDidCopy))
|
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>
|
</button>
|
||||||
<span v-show="showDidCopy">Copied</span>
|
<span v-show="showDidCopy" role="status" aria-live="polite">Copied</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="text-blue-500 text-sm font-bold">
|
<div class="text-blue-500 text-sm font-bold">
|
||||||
@@ -158,7 +171,7 @@
|
|||||||
Your Activity
|
Your Activity
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</section>
|
||||||
|
|
||||||
<!-- Registration notice -->
|
<!-- Registration notice -->
|
||||||
<!--
|
<!--
|
||||||
@@ -169,6 +182,8 @@
|
|||||||
v-if="!isRegistered"
|
v-if="!isRegistered"
|
||||||
id="noticeBeforeAnnounce"
|
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"
|
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">
|
<p class="mb-4">
|
||||||
<b>Note:</b> Before you can publicly announce a new project or time
|
<b>Note:</b> Before you can publicly announce a new project or time
|
||||||
@@ -182,27 +197,31 @@
|
|||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<section
|
||||||
v-if="isRegistered"
|
v-if="isRegistered"
|
||||||
id="sectionNotifications"
|
id="sectionNotifications"
|
||||||
class="bg-slate-100 rounded-md overflow-hidden px-4 py-4 mt-8 mb-8"
|
class="bg-slate-100 rounded-md overflow-hidden px-4 py-4 mt-8 mb-8"
|
||||||
|
aria-labelledby="notificationsHeading"
|
||||||
>
|
>
|
||||||
<!-- label -->
|
<h2 id="notificationsHeading" class="mb-2 font-bold">Notifications</h2>
|
||||||
<div class="mb-2 font-bold">Notifications</div>
|
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<!-- label -->
|
|
||||||
<div>
|
<div>
|
||||||
Reminder Notification
|
Reminder Notification
|
||||||
<font-awesome
|
<button
|
||||||
icon="question-circle"
|
|
||||||
class="text-slate-400 fa-fw ml-2 cursor-pointer"
|
class="text-slate-400 fa-fw ml-2 cursor-pointer"
|
||||||
@click.stop="showReminderNotificationInfo"
|
@click.stop="showReminderNotificationInfo"
|
||||||
/>
|
aria-label="Learn more about reminder notifications"
|
||||||
|
>
|
||||||
|
<font-awesome icon="question-circle" aria-hidden="true"></font-awesome>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<!-- toggle -->
|
|
||||||
<div
|
<div
|
||||||
class="relative ml-2 cursor-pointer"
|
class="relative ml-2 cursor-pointer"
|
||||||
@click="showReminderNotificationChoice()"
|
@click="showReminderNotificationChoice()"
|
||||||
|
role="switch"
|
||||||
|
:aria-checked="notifyingReminder"
|
||||||
|
aria-label="Toggle reminder notifications"
|
||||||
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<!-- input -->
|
<!-- input -->
|
||||||
<input v-model="notifyingReminder" type="checkbox" class="sr-only" />
|
<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">
|
<router-link class="pl-4 text-sm text-blue-500" to="/help-notifications">
|
||||||
Troubleshoot your notifications.
|
Troubleshoot your notifications.
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</section>
|
||||||
<PushNotificationPermission ref="pushNotificationPermission" />
|
<PushNotificationPermission ref="pushNotificationPermission" />
|
||||||
|
|
||||||
<div
|
<section
|
||||||
id="sectionSearchLocation"
|
id="sectionSearchLocation"
|
||||||
class="flex justify-between bg-slate-100 rounded-md overflow-hidden px-4 py-4 mt-8 mb-8"
|
class="flex justify-between bg-slate-100 rounded-md overflow-hidden px-4 py-4 mt-8 mb-8"
|
||||||
|
aria-labelledby="searchLocationHeading"
|
||||||
>
|
>
|
||||||
<!-- label -->
|
<h2 id="searchLocationHeading" class="mb-2 font-bold">Location for Searches</h2>
|
||||||
<span class="mb-2 font-bold">Location for Searches</span>
|
|
||||||
<router-link
|
<router-link
|
||||||
:to="{ name: 'search-area' }"
|
: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"
|
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…
|
{{ isSearchAreasSet ? "Change" : "Set" }} Search Area…
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</section>
|
||||||
|
|
||||||
<!-- User Profile -->
|
<!-- User Profile -->
|
||||||
<div
|
<section
|
||||||
v-if="isRegistered"
|
v-if="isRegistered"
|
||||||
class="bg-slate-100 rounded-md overflow-hidden px-4 py-4 mt-8 mb-8"
|
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">
|
<h2 id="userProfileHeading" class="flex items-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">
|
|
||||||
<span class="font-bold">Public Profile</span>
|
<span class="font-bold">Public Profile</span>
|
||||||
<font-awesome
|
<button
|
||||||
icon="circle-info"
|
|
||||||
class="text-slate-400 fa-fw ml-2 cursor-pointer"
|
class="text-slate-400 fa-fw ml-2 cursor-pointer"
|
||||||
@click="showProfileInfo"
|
@click="showProfileInfo"
|
||||||
/>
|
aria-label="Learn more about public profile"
|
||||||
</div>
|
>
|
||||||
|
<font-awesome icon="circle-info" aria-hidden="true"></font-awesome>
|
||||||
|
</button>
|
||||||
|
</h2>
|
||||||
<textarea
|
<textarea
|
||||||
v-model="userProfileDesc"
|
v-model="userProfileDesc"
|
||||||
class="w-full h-32 p-2 border border-slate-300 rounded-md"
|
class="w-full h-32 p-2 border border-slate-300 rounded-md"
|
||||||
placeholder="Write something about yourself for the public..."
|
placeholder="Write something about yourself for the public..."
|
||||||
:readonly="loadingProfile || savingProfile"
|
:readonly="loadingProfile || savingProfile"
|
||||||
:class="{ 'bg-slate-100': loadingProfile || savingProfile }"
|
:class="{ 'bg-slate-100': loadingProfile || savingProfile }"
|
||||||
|
aria-label="Public profile description"
|
||||||
|
:aria-busy="loadingProfile || savingProfile"
|
||||||
></textarea>
|
></textarea>
|
||||||
|
|
||||||
<div class="flex items-center mb-4" @click="toggleUserProfileLocation">
|
<div class="flex items-center mb-4" @click="toggleUserProfileLocation">
|
||||||
@@ -364,18 +381,19 @@
|
|||||||
</div>
|
</div>
|
||||||
<div v-else-if="loadingProfile">Loading...</div>
|
<div v-else-if="loadingProfile">Loading...</div>
|
||||||
<div v-else>Saving...</div>
|
<div v-else>Saving...</div>
|
||||||
</div>
|
</section>
|
||||||
|
|
||||||
<div
|
<section
|
||||||
v-if="activeDid"
|
v-if="activeDid"
|
||||||
id="sectionUsageLimits"
|
id="sectionUsageLimits"
|
||||||
class="bg-slate-100 rounded-md overflow-hidden px-4 py-4 mt-8 mb-8"
|
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 -->
|
<!-- 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…
|
Checking…
|
||||||
<font-awesome icon="spinner" class="fa-spin"></font-awesome>
|
<font-awesome icon="spinner" class="fa-spin" aria-hidden="true"></font-awesome>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-4 text-center">
|
<div class="mb-4 text-center">
|
||||||
{{ limitsMessage }}
|
{{ limitsMessage }}
|
||||||
@@ -423,7 +441,7 @@
|
|||||||
>
|
>
|
||||||
Recheck Limits
|
Recheck Limits
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</section>
|
||||||
|
|
||||||
<DataExportSection :active-did="activeDid" />
|
<DataExportSection :active-did="activeDid" />
|
||||||
|
|
||||||
@@ -435,7 +453,8 @@
|
|||||||
>
|
>
|
||||||
Advanced
|
Advanced
|
||||||
</h3>
|
</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">
|
<p class="text-rose-600 mb-8">
|
||||||
Beware: the features here can be confusing and even change data in ways
|
Beware: the features here can be confusing and even change data in ways
|
||||||
you do not expect. But we support your freedom!
|
you do not expect. But we support your freedom!
|
||||||
@@ -606,41 +625,60 @@
|
|||||||
|
|
||||||
<div id="sectionClaimServer">
|
<div id="sectionClaimServer">
|
||||||
<h2 class="text-slate-500 text-sm font-bold mt-4">Claim Server</h2>
|
<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
|
<input
|
||||||
|
id="apiServerInput"
|
||||||
v-model="apiServerInput"
|
v-model="apiServerInput"
|
||||||
type="text"
|
type="text"
|
||||||
class="block w-full rounded border border-slate-400 px-4 py-2"
|
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
|
<button
|
||||||
v-if="apiServerInput != apiServer"
|
v-if="apiServerInput != apiServer"
|
||||||
class="w-full px-4 rounded bg-yellow-500 border border-slate-400"
|
class="w-full px-4 rounded bg-yellow-500 border border-slate-400"
|
||||||
@click="onClickSaveApiServer()"
|
@click="onClickSaveApiServer()"
|
||||||
|
aria-label="Save API server URL"
|
||||||
>
|
>
|
||||||
<font-awesome
|
<font-awesome
|
||||||
icon="floppy-disk"
|
icon="floppy-disk"
|
||||||
class="fa-fw"
|
class="fa-fw"
|
||||||
color="white"
|
color="white"
|
||||||
|
aria-hidden="true"
|
||||||
></font-awesome>
|
></font-awesome>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<div class="mt-2" role="group" aria-label="Quick server selection">
|
||||||
class="px-3 rounded bg-slate-200 border border-slate-400"
|
<button
|
||||||
@click="apiServerInput = AppConstants.PROD_ENDORSER_API_SERVER"
|
class="px-3 rounded bg-slate-200 border border-slate-400"
|
||||||
>
|
@click="apiServerInput = AppConstants.PROD_ENDORSER_API_SERVER"
|
||||||
Use Prod
|
aria-label="Use production server URL"
|
||||||
</button>
|
>
|
||||||
<button
|
Use Prod
|
||||||
class="px-3 rounded bg-slate-200 border border-slate-400"
|
</button>
|
||||||
@click="apiServerInput = AppConstants.TEST_ENDORSER_API_SERVER"
|
<button
|
||||||
>
|
class="px-3 rounded bg-slate-200 border border-slate-400"
|
||||||
Use Test
|
@click="apiServerInput = AppConstants.TEST_ENDORSER_API_SERVER"
|
||||||
</button>
|
aria-label="Use test server URL"
|
||||||
<button
|
>
|
||||||
class="px-3 rounded bg-slate-200 border border-slate-400"
|
Use Test
|
||||||
@click="apiServerInput = AppConstants.LOCAL_ENDORSER_API_SERVER"
|
</button>
|
||||||
>
|
<button
|
||||||
Use Local
|
class="px-3 rounded bg-slate-200 border border-slate-400"
|
||||||
</button>
|
@click="apiServerInput = AppConstants.LOCAL_ENDORSER_API_SERVER"
|
||||||
|
aria-label="Use local server URL"
|
||||||
|
>
|
||||||
|
Use Local
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<label
|
<label
|
||||||
@@ -878,8 +916,8 @@
|
|||||||
>
|
>
|
||||||
View Logs
|
View Logs
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</section>
|
||||||
</section>
|
</main>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<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 endorserTermInConfig = endorserTerm?.substring(ENDORSER_ENV_NAME.length + 1);
|
||||||
|
|
||||||
const endorserServer = endorserTermInConfig || 'https://test-api.endorser.ch';
|
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 }) => {
|
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