Test Cases
Every test case a QA tester needs to verify the iSpeaker Live MVP — grouped by module, tagged by type and priority.
On this page
- About these test cases
- How to use this catalog
- Legend & conventions
- Authentication & Roles
- Profile
- Social Feed & Reactions
- Follow System
- Courses & Lessons
- Books & Reader
- Live Rooms
- Consultations
- Wallet & Payments
- Cart & Marketplace
- Chat & Messaging
- Gifts
- Notifications
- Admin Panel
- Localization (AR/EN)
- Accessibility
- Performance
- Security
- Settings, Search, Dashboard
About these test cases
This is the master catalog of test cases for iSpeaker Live MVP 1.0 during the current Testing Phase. Cases are grouped by module and tagged with a type, priority, and module label so testers can build smoke, regression, and exploratory runs from one source of truth.
How to use this catalog
- Pick a module section that matches the build under test.
- For each case: set up the Preconditions, follow the Steps in order, then verify each Expected result independently.
- Mark the case Pass only when every expected result is observed. Any deviation is a Fail and must be logged as a bug with the case ID referenced.
- For new bugs, attach screenshots / screen recordings and the device + build number.
- Use Test data values when listed. If no values are listed, use realistic data and document what you used in the run notes.
Legend & conventions
Priority
Critical Blocks release if it fails. High Important business or security path. Medium Significant user-facing behavior. Low Cosmetic / minor convenience.
Type
Functional Integration UI/UX Security Performance Accessibility i18n Negative API
ID prefixes
TC-AUTH-*auth & roles ·TC-PRF-*profile ·TC-FEED-*social feedTC-FLW-*follow ·TC-CRS-*courses ·TC-BK-*booksTC-LR-*live rooms ·TC-CON-*consultations ·TC-PAY-*wallet/paymentsTC-CART-*cart ·TC-CHAT-*chat ·TC-GIFT-*gifts ·TC-NTF-*notificationsTC-ADM-*admin ·TC-I18N-*localization ·TC-A11Y-*accessibilityTC-PERF-*performance ·TC-SEC-*security ·TC-SET-*/TC-SRCH-*/TC-DASH-*misc.
Authentication & Roles
Registration, login, password recovery, role switching, and session integrity.
- Preconditions
- App or web is open at the registration screen.
- Email
tester01@example.comis not yet registered. - Phone
+966500000001is not yet registered.
- Test data
Name: Nora TestEmail: tester01@example.comPhone: +966500000001Password: Test@1234Language: Arabic
- Steps
- Open the registration page.
- Fill in name, email, phone, and password fields with the test data above.
- Select Student as account type (or accept default).
- Accept the terms-of-service checkbox.
- Tap/click the Register button.
- Expected results
- Account is created successfully.
- A welcome notification is shown in Arabic (default language).
- User is redirected to the home feed.
- The
userstable contains a new row withis_verified = 0. - A
walletsrow with balance0.00is auto-created. - A
user_profilesrow withlanguage = aris auto-created.
- Postconditions
- Test account exists and is logged in.
- Bearer token is stored on the client.
- Preconditions
- An account with
tester01@example.comalready exists.
- An account with
- Steps
- Open the registration page.
- Enter the same email
tester01@example.com. - Fill in the rest of the fields with valid data.
- Submit the form.
- Expected results
- Submission is blocked with a clear validation error.
- Error message reads "The email has already been taken." in English, or its Arabic equivalent.
- No new row is created in the
userstable. - HTTP response is
422 Unprocessable Entitywith the field nameemail.
- Preconditions
- Valid verified account exists.
- Test data
email: tester01@example.compassword: Test@1234
- Steps
- Open the login screen.
- Enter the email and password.
- Tap Login.
- Expected results
- Response 200 OK is returned.
- JSON contains
tokenanduserobjects. - Token is persisted to secure storage (mobile) or HTTP-only storage (web).
- User lands on the feed with their name visible in the topbar.
- A new row appears in
personal_access_tokens.
- Steps
- Enter a valid email but wrong password.
- Submit the form.
- Expected results
- Login is rejected.
- A generic error like "Invalid credentials" is shown (no hint about which field is wrong).
- No token is issued.
- After 5 consecutive failures the account is temporarily rate-limited.
- Notes
- Verify that the error does not leak whether the email exists — this is a security requirement.
- Steps
- On the login screen, tap Forgot password?.
- Enter a registered email address.
- Submit the request.
- Expected results
- A success toast is shown.
- A row is created in
password_reset_tokens. - An email is sent to the address with a reset link valid for 60 minutes.
- The reset link works only once.
- Preconditions
- User is logged in with a valid Sanctum token.
- Steps
- Open the settings menu.
- Tap Logout.
- After confirmation, try to call any protected endpoint with the old token (using a test client).
- Expected results
- The user is returned to the login screen.
- The local token is cleared.
- The corresponding row in
personal_access_tokensis deleted. - Reusing the old token returns 401 Unauthorized.
- Preconditions
- User is a Student with no active Speaker role.
- Steps
- Open the profile menu.
- Tap Become a Speaker.
- Fill the speaker profile form (headline, expertises, bio).
- Submit.
- Expected results
- A new role assignment is created in
model_has_roles. - Speaker dashboard becomes accessible from the profile menu.
- The user still has the Student role and can still consume content.
- Speaker-only nav items (My Courses, Consultations, Earnings) become visible.
- A new role assignment is created in
- Notes
- Admin role is granted only via Filament and cannot be self-assigned.
- Steps
- Login on Device A and copy the Sanctum token.
- Login on Device B and change the password.
- On Device A, attempt to call
/api/profilewith the old token.
- Expected results
- Old tokens are invalidated upon password change.
- Device A receives 401 and is redirected to login.
Profile
Editing profile data, avatar uploads, speaker fields, and privacy.
- Preconditions
- User is logged in.
- Steps
- Open Edit profile.
- Update bio, headline, location, and timezone.
- Toggle Show email off.
- Save changes.
- Refresh the profile page.
- Expected results
- Changes are saved without errors.
- On refresh, all new values are present.
user_profiles.show_emailis0.- Email is no longer visible on the public profile page.
- Steps
- Open Edit profile.
- Tap on avatar area and select a JPG ≤ 2 MB.
- Save.
- Try again with a PDF file.
- Expected results
- JPG upload succeeds; new avatar is shown across the app within 5 seconds.
- PDF upload is rejected with a clear validation message ("Image must be JPG/PNG/WebP, max 2MB").
- No partial file ends up in storage.
- Preconditions
- User has Speaker role.
- Steps
- Open the Speaker profile editor.
- Add one experience entry (title, company, start date, "I currently work here").
- Add one certificate entry (title, issuer, issue date, credential URL).
- Save.
- Expected results
- Both entries appear on the public profile in chronological order.
user_experiencesanduser_certificatesrows exist for the user.- Reordering entries (drag/drop) updates the
ordercolumn.
- Steps
- User A toggles "Private profile" ON.
- User B (not following A) opens A's profile.
- Expected results
- B sees A's basic info (name, avatar, headline) only.
- Posts, followers, and following lists are hidden behind a "Follow to see content" panel.
- B can send a follow request.
Social Feed & Reactions
Posts, comments, reactions, sharing, and moderation.
- Steps
- Open the home feed.
- Tap the Create post composer.
- Type "Hello iSpeaker Live!".
- Tap Publish.
- Expected results
- Post appears at the top of the feed instantly (optimistic UI).
- On refresh, the post persists.
- A
postsrow withpost_type = "text"is created. - Other followers receive the new post in their feed.
- Steps
- Open the composer.
- Attach a single JPG ≤ 5 MB.
- Add caption "Test image post".
- Publish.
- Expected results
posts.post_type = "image".media_urlscontains the uploaded image URL.- Image renders with the correct aspect ratio in the feed.
- Tapping the image opens a fullscreen viewer.
- Steps
- Open any post.
- Long-press the reaction button and pick "Love".
- Tap again and switch to "Haha".
- Tap again to remove the reaction.
- Expected results
- Reaction count updates correctly each step.
- The
reactionsunique index (user_id, reactable_id, reactable_type) prevents duplicates. - Removing leaves no reaction row for the user on the post.
- Preconditions
- Post belongs to user A. User B will comment.
- Steps
- User B opens user A's post.
- Adds a comment "Nice post!".
- User A opens notifications.
- Expected results
commentstable has a new row.posts.comments_countis incremented.- A
notificationsrow for user A is created with typecomment. - User A receives a push notification (FCM) and an in-app real-time notification (Reverb).
- Steps
- Open any post.
- Tap the menu and choose Report.
- Pick "Harassment" and add a short description.
- Submit.
- Expected results
post_reportsrow is inserted with statuspending.- The reporter sees a confirmation toast.
- The same user reporting the same post again replaces or merges the report (no duplicates).
- The report appears in the Filament admin panel for review.
- Steps
- Open a post and tap Share.
- Choose Share to feed.
- Add comment "Worth a read".
- Submit.
- Expected results
post_sharesrow is created.posts.shares_countis incremented.- The shared item shows on the sharer's profile feed with the comment on top.
- Preconditions
- Feed has at least 80 posts available.
- Steps
- Open the feed.
- Scroll to the bottom three times.
- Expected results
- Each scroll triggers a paginated request returning 20 items.
- No duplicate posts are loaded.
- p95 response time ≤ 600 ms on staging.
Follow System
Direct follow, follow requests for private accounts, unfollow.
- Steps
- User B opens user A's public profile.
- Taps Follow.
- Expected results
followersrow is created (B follows A).user_profiles.followers_countfor A is incremented.user_profiles.following_countfor B is incremented.- A is notified that B started following them.
- Preconditions
- User A has private profile.
- Steps
- User B taps Follow on A's profile.
- A opens follow requests and accepts B.
- Expected results
- After the tap: a row in
follow_requestswithstatus = pending. - A receives a notification "B requested to follow you".
- On accept: the row's
statusbecomesaccepted, afollowersrow is created, and counters update. - B receives a notification "A accepted your follow request".
- After the tap: a row in
- Steps
- B is following A.
- B taps Unfollow.
- Expected results
- The
followersrow is deleted. - Both counters decrement by 1.
- No notification is sent on unfollow.
- The
Courses & Lessons
Authoring, enrollment, playback, progress, and certification.
- Preconditions
- User has Speaker role.
- Steps
- Open Create course.
- Fill title, subtitle, description, language=AR, level=Beginner, price=99.00.
- Add one section ("Module 1").
- Add one lesson (title, upload an MP4, set order=1).
- Add a course thumbnail.
- Save as draft.
- Use the preview, then click Publish.
- Expected results
courses.statusmoves fromdrafttopublished.- Course appears in the public courses listing.
total_lessonsandtotal_duration_minutesreflect the added lesson.
- Steps
- Create a draft course without any lessons.
- Click Publish.
- Expected results
- Publish is blocked.
- A clear error explains "Course must contain at least one section with one lesson."
statusremainsdraft.
- Preconditions
- A published free course exists.
- Steps
- Open the course detail page.
- Tap Enroll for free.
- Expected results
- Enrollment is immediate; the user lands on the course player.
enrollmentsrow withprice_paid = 0andstatus = active.courses.students_countis incremented.- No
transactionsrow is created.
- Preconditions
- Course price is 99 SAR. Student wallet balance is ≥ 99 SAR.
- Steps
- Open the paid course.
- Tap Buy now and choose Pay from wallet.
- Confirm.
- Expected results
- A
transactionsdebit row of 99 is created for the student. - A
transactionscredit row of (99 minus platform fee) is created for the speaker (after release period if applicable). - Student wallet balance decreases by 99; speaker pending_balance increases.
enrollmentsrow is created.invoicesrow is created with a unique invoice number and a downloadable PDF URL.
- A
- Preconditions
- User is enrolled in a course with a 10-minute video lesson.
- Steps
- Open the lesson and play until 3:30.
- Close the app.
- Re-open the course and re-open the same lesson.
- Expected results
lesson_progress.watched_duration_seconds≈ 210.- Video resumes from the saved timestamp ± 2 seconds.
- Lesson is marked complete once the user reaches ≥ 95% of duration.
- Preconditions
- User is 99% through a course.
- Steps
- Complete the final lesson.
- Expected results
enrollments.statusbecomescompleted.- A
certificatesrow is created with a uniquecertificate_numberand verification URL. - User receives a notification and an email.
- Visiting the verification URL displays the certificate page.
- Steps
- Inside a video lesson at 1:24, open the Notes tab.
- Add a note "Important formula".
- Save.
- Close and re-open the lesson.
- Expected results
lesson_notesrow stored withtimestamp_seconds ≈ 84.- On reopen, the note is listed in the Notes tab.
Books & Reader
Publishing, previews, annotations, progress tracking, DRM-like protections.
- Preconditions
- User has Speaker role.
- Steps
- Open Publish a book.
- Fill title, author, description, price, category.
- Upload a PDF (≤ 50 MB).
- Set "preview pages" to 5.
- Disable printing, disable copying.
- Publish.
- Expected results
books.statusbecomespublished.page_countis auto-detected from the PDF.- Book appears in the books listing.
- On the public reader page, only the first 5 pages are accessible before purchase.
- Steps
- Open a paid book detail page as a guest student.
- Tap Read preview.
- Scroll past page 5.
- Expected results
- Pages 1-5 render correctly.
- Page 6 onwards is blurred with a "Buy to continue reading" overlay.
- Preconditions
- User has purchased the book.
- Steps
- Open page 12, highlight a sentence and pick yellow.
- Add a note on page 14.
- Bookmark page 18.
- Close the reader.
- Re-open the book.
- Expected results
- Three
book_annotationsrows exist with correctannotation_type. - Annotations render on re-open.
- Bookmarks list shows page 18.
- Three
- Steps
- Open the book and read to page 23.
- Close the app.
- Re-open the book from the library.
- Expected results
book_reading_progress.current_pageis 23.- Reader opens at page 23.
reading_time_minutesincreases on each session.
- Preconditions
- Book has
allow_printing = 0andallow_copying = 0.
- Book has
- Steps
- On web, try CTRL/CMD+P.
- Try selecting text and copying.
- Expected results
- Print dialog is suppressed or shows a "Printing disabled" page.
- Text selection is disabled; copy returns empty clipboard.
- Notes
- Note: client-side protections only — combine with watermarks for legal deterrence.
Live Rooms
Scheduling, registration, Jitsi sessions, in-room chat, lifecycle.
- Preconditions
- User has Speaker role.
- Steps
- Open Create live room.
- Set title, description, category.
- Pick start date = next Monday, end date = next Monday.
- Pick start time = 19:00, end time = 20:00.
- Duration type = day, access type = free.
- Save.
- Expected results
live_roomsrow created withstatus = scheduled.- Room appears on the speaker dashboard and on the public upcoming rooms list.
- Joining is blocked before 5 minutes before
start_time.
- Preconditions
- Paid room with price 50 SAR. Student wallet has ≥ 50 SAR.
- Steps
- Open the room detail page.
- Tap Register and pay from wallet.
- Expected results
live_room_registrationsrow withstatus = registeredandprice_paid = 50.- Transaction is recorded.
participants_countincrements.
- Preconditions
- Scheduled room exists; speaker is the owner.
- Steps
- Within 5 minutes of start_time, speaker opens the room and taps Start session.
- Expected results
current_jitsi_room_idis populated.statusbecomesactive.- Jitsi iframe (web) or native module (mobile) connects to the new room ID.
- Registered students see a "Join now" call-to-action on their dashboards.
- Preconditions
- Room is active with at least 2 participants.
- Steps
- User A types "hello" in the room chat and presses send.
- Observe user B's screen.
- Expected results
live_room_messagesrow created.- User B sees "hello" within 1 second via the Reverb channel.
- Pinning a message updates
is_pinnedand re-broadcasts.
- Steps
- Speaker taps End session.
- Expected results
statusbecomescompleted.- Participants are disconnected gracefully.
total_sessionsincrements.
Consultations
Availability, bookings, payments, completion, and reviews.
- Preconditions
- User has Speaker role.
- Steps
- Open Consultation availability.
- Add a Monday slot 17:00-19:00, 60-minute sessions, 100 SAR per session.
- Save.
- Expected results
consultation_availabilitiesrow exists.- Student booking calendar shows two slots on Monday: 17:00 and 18:00.
- Steps
- Open the speaker's profile and tap Book consultation.
- Pick the 17:00 slot on next Monday.
- Pay from wallet.
- Expected results
consultation_bookingsrow withstatus = scheduled.- The slot is no longer offered to other students for that timestamp.
- A confirmation notification is sent to both parties.
- Preconditions
- Booking is past its scheduled time and never started.
- Steps
- Speaker opens the booking and marks it No-show (student).
- Expected results
statusupdates tono_show.- Refund rule defined by admin is applied (full / partial / none).
- Both parties are notified.
- Steps
- Booking is in
completedstate. - Student submits a 5-star review.
- Student tries to submit a second review.
- Booking is in
- Expected results
- First review is saved in
consultation_ratings. - Second attempt is blocked with "You have already reviewed this session".
- A user not in the booking cannot submit a review at all.
- First review is saved in
Wallet & Payments
Top-ups, purchases, withdrawals, refunds, VAT/invoicing.
- Preconditions
- PayPal sandbox is configured and accessible.
- Steps
- Open Wallet and tap Top up.
- Choose 100 SAR.
- Complete payment in PayPal sandbox.
- Return to the app.
- Expected results
- A pending
transactionsrow is created when initiated. - On webhook confirmation, the row's
statusmoves tocompleted. wallets.balanceincreases by 100.- Invoice is generated with 15% VAT line.
- A pending
- Steps
- Initiate a top-up.
- Cancel the PayPal flow before approval.
- Expected results
transactions.statusremainspending(or moves tofailed).- Wallet balance is unchanged.
- Pending transaction expires after the configured TTL.
- Preconditions
- Speaker wallet has settled balance ≥ minimum threshold.
- Steps
- Open Wallet and tap Withdraw.
- Choose method, enter amount, submit.
- Expected results
- A debit
transactionsrow is created withstatus = pending. pending_balanceincreases;balancedecreases.- Admin sees the request in Filament and can approve/reject.
- A debit
- Preconditions
- Wallet balance = 100 SAR. Two pending items in cart, 60 SAR each.
- Steps
- From two devices simultaneously, attempt to purchase each item via wallet.
- Expected results
- Only one purchase succeeds; the other returns an "Insufficient funds" error.
- Final balance is ≥ 0 (never negative).
- Transaction rows reflect the correct outcome.
- Notes
- Verify Laravel uses a row lock / atomic update on wallets table during debit.
- Steps
- Make a 100 SAR purchase.
- Open the generated invoice.
- Expected results
- Subtotal = 100, VAT = 15, Total = 115 (or per the configured rate).
- Invoice PDF renders the same numbers as the transaction record.
Cart & Marketplace
Cart contents, unique items, multi-item checkout.
- Steps
- Open any paid course.
- Tap Add to cart.
- Expected results
cart_itemsrow withcartable_type=Courseis created.- Cart icon badge increases by 1.
- Adding the same item twice does not duplicate (unique index).
- Preconditions
- Cart has 1 course (50) and 1 book (30). Wallet has ≥ 80 + VAT.
- Steps
- Open cart and tap Checkout.
- Pay from wallet.
- Expected results
- Both items become enrollment / purchase rows.
- Cart is empty after success.
- Single invoice consolidates both items.
Chat & Messaging
1:1 conversations, media, replies, read receipts.
- Steps
- Open user B's profile and tap Message.
- Type "hello" and send.
- Expected results
- A
conversationsrow is created (or existing one is reused). - A
messagesrow is created. - User B sees the message in real time via Reverb.
- Unread badge updates on B's side.
- A
- Steps
- In an existing conversation, attach an image.
- Send.
- Long-press the sent message and choose Reply.
- Type a reply and send.
- Expected results
- First message has
type = imageand amedia_urlsentry. - Second message has
reply_to_idset. - Reply renders the quoted parent message.
- First message has
- Steps
- B opens the conversation with A.
- Expected results
conversations.user_*_last_read_atupdates for B.- A sees double-tick read indicator.
Gifts
Sending paid gifts on posts and rooms; wallet effects.
- Preconditions
- Sender wallet has enough balance.
- Steps
- Open a post.
- Tap the gift icon, pick a gift, set quantity = 2.
- Confirm.
- Expected results
gift_transactionsrow is created withquantity = 2.- Sender wallet decreases by
2 × gift.price. - Receiver wallet pending_balance increases by the platform-defined share.
- A gift animation plays on the post.
Notifications
In-app real-time and push (FCM) notifications and deep linking.
- Steps
- User A comments on user B's post.
- Observe user B's notification panel.
- Expected results
- Bell icon shows a red dot within 1 second.
- Tapping the bell shows the notification at the top.
- A
notificationsrow exists with the correcttypeandactor_id.
- Steps
- Open the notifications panel.
- Tap Mark all as read.
- Expected results
- All
read_atcolumns for unread rows are set. - Bell badge clears.
- All
- Preconditions
- FCM token is registered on the device.
- Steps
- Trigger a push (e.g., new follower).
- Tap the notification when the app is in background.
- Expected results
- App opens at the relevant screen (e.g., the new follower's profile).
- Deep link includes the correct entity ID.
Admin Panel
Filament admin authentication, moderation actions, settings.
- Preconditions
- Admin account exists in
userswith the admin role assigned.
- Admin account exists in
- Steps
- Visit
/admin. - Enter admin credentials.
- Visit
- Expected results
- Login succeeds; Filament dashboard renders.
- Non-admin users are redirected away from the panel.
- Steps
- Open Post reports.
- Open a pending report.
- Choose Resolve - hide post.
- Expected results
- Report
statusbecomesresolved. - Post is soft-deleted or hidden depending on action.
- Reporter and post author both receive a notification.
- Report
- Steps
- Open Settings.
- Change VAT rate from 15 to 16.
- Save.
- Expected results
settingsrow is updated.- New invoices use the new rate.
- Existing invoices remain unchanged.
Localization (Arabic / English)
Language switching, RTL flips, locale-aware formatting, translation coverage.
- Steps
- Open settings and switch language to English.
- Expected results
- All labels switch to English on the next render.
user_profiles.languageupdates toen.- Layout direction switches from RTL to LTR.
- Date/time formats follow the new locale.
- Steps
- In Arabic mode, navigate the home, profile, and chat screens.
- Inspect arrows, badges, and progress bars.
- Expected results
- Back arrows point right.
- Drawer slides from the right.
- Progress bars fill from right to left.
- No clipped text or misplaced icons.
- Steps
- Compare 1,234.50 SAR display in EN vs AR.
- Expected results
- Arabic uses Arabic-Indic digits if configured, otherwise Western digits.
- Currency symbol/code position matches the locale (SAR vs ر.س).
- Steps
- Walk through all main screens in both languages.
- Expected results
- No raw keys like
auth.failedor{{count}}are visible. - All placeholders are correctly interpolated.
- No raw keys like
Accessibility
Keyboard navigation, screen reader support, color contrast.
- Steps
- On the login and registration forms, use TAB to move through fields.
- Expected results
- Focus visits fields in visual order.
- Submit button is reachable with TAB.
- Focus ring is visible on every focusable element.
- Preconditions
- VoiceOver / TalkBack / NVDA is active.
- Steps
- Submit the login form with empty fields.
- Expected results
- Each error is announced via
aria-liveregion. - Error text is associated with its field via
aria-describedby.
- Each error is announced via
- Steps
- Use a contrast checker on primary buttons, body text, and error text in light and dark themes.
- Expected results
- Body text vs background ≥ 4.5:1.
- Large text and UI elements ≥ 3:1.
Performance
Startup time, scroll smoothness, API latency under load.
- Steps
- Force-close the app, then re-launch.
- Measure time to interactive home feed.
- Expected results
- Time to interactive ≤ 3 s on mid-range device with 4G.
- Steps
- Scroll the home feed rapidly for 30 seconds.
- Expected results
- Average FPS > 50 with no perceptible jank.
- No memory leaks after the test (heap stable).
- Steps
- Run k6/JMeter against staging with 50 concurrent users for 5 minutes on key endpoints.
- Expected results
- p95 latency ≤ 600 ms on read endpoints.
- Error rate < 1%.
Security
Authorization, XSS, rate limiting, signed media URLs.
- Steps
- User A creates post P.
- User B (different account) calls
PUT /api/posts/{P.id}.
- Expected results
- Response is 403 Forbidden.
- Post P is unchanged.
- Steps
- Create a post with content
<script>alert(1)</script>.
- Create a post with content
- Expected results
- The post renders the literal text; no alert fires.
- Stored content is sanitized.
- Steps
- Hit the login endpoint 20 times in one minute with wrong credentials.
- Expected results
- After the threshold, responses are 429 Too Many Requests for the remaining attempts.
- Steps
- Open a course video as an enrolled user.
- Copy the video URL and open it in an incognito window.
- Expected results
- Signed URL works within its TTL.
- Same URL after expiry returns 403.
Settings, Search & Dashboard
Preferences persistence, global search, dashboard accuracy.
- Steps
- Open Settings → Notifications.
- Toggle "Email — comments" off.
- Save and have another user comment on your post.
- Expected results
- Setting is persisted.
- No email is sent for comments.
- In-app notification still appears.
- Steps
- In the search bar, type "Nora".
- Expected results
- Results are grouped (Users / Courses / Books / Rooms).
- Tapping a result navigates to the right page.
- Empty queries do not trigger a request.
- Steps
- Open the Speaker dashboard.
- Expected results
- Total students, total revenue, and pending balance match the database aggregates.
- Charts render without errors.
Exit checklist for the Testing Phase
- 100% of Critical test cases pass on the target release build.
- ≥ 95% of High test cases pass. Each failing High has a documented workaround or a deferred-to-1.1 decision.
- No open Severity-1 or Severity-2 bugs.
- Bilingual smoke pass (AR & EN) executed on web, Android, and iOS.
- Performance budgets met on staging (see
TC-PERF-*). - Sign-off recorded by Product, QA Lead, and Tech Lead.