Skip to content

E2E SCENARIOS — GARAGES, STAFF, ROLES & GROUPS

VietGara — Release 1.0–1.1 · Version 1.0 — September 28, 2026

Covers FR-TEN-01 to 05, FR-IAM-03 and FR-NOT-04 (invitation e-mails), plus tenant isolation (NFR security). Users, garages and roles are in 00_Test_Data.md.

IDTitlePriorityAutomation
E2E-TEN-001Create the first garageP1Automated (API, partial)
E2E-TEN-002Edit garage profile, configuration and opening hoursP2Automated (API, partial)
E2E-TEN-003Upload, replace and remove the garage logoP2Automated (API, partial)
E2E-TEN-004A second garage beyond the trial limit is refusedP1Automated (API, partial)
E2E-TEN-005Switch the active garageP1Automated (API, partial)
E2E-TEN-006Deactivate and reactivate a garageP1Automated (API, partial)
E2E-TEN-007Invite an existing user by e-mail and accept from the e-mail linkP1Automated (API, partial)
E2E-TEN-008Invite a user by phone numberP2Automated (API, partial)
E2E-TEN-009Invitation lookups that cannot be invitedP2Automated (API, partial)
E2E-TEN-010Decline an invitationP2Automated (API, partial)
E2E-TEN-011Revoke a pending invitationP2Automated (API, partial)
E2E-TEN-012An expired invitation cannot be acceptedP3Automated (API, partial)
E2E-TEN-013An invitation link opened by another userP3Automated (API, partial)
E2E-TEN-014Change a member’s groups; permissions change at onceP1Automated (API, partial)
E2E-TEN-015Deactivate, reactivate and remove a memberP1Automated (API, partial)
E2E-TEN-016A manager can manage staff but not roles or the subscriptionP2Automated (API, partial)
E2E-TEN-017Create a custom role in the role editorP1Automated (API, partial)
E2E-TEN-018Role editor: unsaved changes, undo and reset to defaultP2Automated (API, partial)
E2E-TEN-019Role and group names must be unique; a role in use cannot be deletedP2Automated (API, partial)
E2E-TEN-020Staff groups of a garageP2Automated (API, partial)
E2E-TEN-021Staff in two garages has separate permissions in eachP2Automated (API, partial)
E2E-TEN-022One owner cannot reach another owner’s garageP1Automated (API, partial)
E2E-TEN-023Document numbering template validationP3Automated (API, partial)

E2E-TEN-001 · Create the first garage

Priority: P1 · Covers: FR-TEN-01, FR-IAM-03 · Automation: Automated (API, partial)

Preconditions: [email protected] exists, is verified and has no garage (from E2E-IAM-001).

Test data: garage QA Gara IAM001 Go Vap, address 78 Quang Trung, Go Vap, TP.HCM, phone 0283000901.

Steps

  1. Sign in as [email protected]. Garages shows the empty state.
  2. Press Add garage, enter the test data and create.
  3. Open Roles & groups.

Expected results

  • The garage appears in the list with the Owner tag and status Active, and becomes the active garage in the header selector.
  • The sidebar shows Customers, Vehicles, Suppliers and Staff.
  • Roles & groups shows “Groups in QA Gara IAM001 Go Vap” with the five default groups (Manager, Accountant, Service Advisor, Technician, Inventory Staff), each using the role of the same name, with 0 members.
  • Subscription shows usage Garages 1 / 1.

E2E-TEN-002 · Edit garage profile, configuration and opening hours

Priority: P2 · Covers: FR-TEN-01, FR-TEN-05 · Automation: Automated (API, partial)

Preconditions: baseline. Signed in as OWNER_A.

Test data: garage A2 (QA Gara A2 Thu Duc). New phone 0283000199, tax ID 0312345678. Opening hours: Monday–Saturday 07:30–17:30, Sunday closed.

Steps

  1. Garages → Garage settings of QA Gara A2 Thu Duc.
  2. Profile card: change the phone and tax ID, save.
  3. Configuration card: check the defaults, then save without changes.
  4. Opening hours card: set the hours from the test data, save.
  5. Reload the page.

Expected results

  • Each card saves on its own and shows “Changes saved.”; saving one card does not reset another.
  • Configuration defaults: time zone Asia/Ho_Chi_Minh, currency VND.
  • After the reload, the phone, tax ID and hours are shown as saved; Sunday is Closed.

Priority: P2 · Covers: FR-TEN-01 · Automation: Automated (API, partial)

Preconditions: baseline. Signed in as OWNER_A.

Test data: files kept with the test assets: qa-logo-ok.png (PNG, ~200 KB), qa-logo-ok.webp (WebP, ~100 KB), qa-logo-too-big.jpg (JPEG, ~1.5 MB), qa-logo-not-image.pdf (PDF, renamed to .png: qa-logo-fake.png).

Steps

  1. Settings of QA Gara A1 Quan 1 → Logo card → Upload logo with qa-logo-ok.png.
  2. Open Garages and the header garage selector.
  3. Upload qa-logo-ok.webp.
  4. Upload qa-logo-too-big.jpg, then qa-logo-fake.png.
  5. Press Remove logo.

Expected results

  • Step 1: the preview shows the logo; the Garages list and the selector show it too (step 2).
  • Step 3 replaces it.
  • Step 4: both are refused with “The logo must be a PNG, JPEG or WebP image of at most 1 MB.”; the WebP logo stays.
  • Step 5: the logo is gone everywhere; the garage shows its placeholder.

E2E-TEN-004 · A second garage beyond the trial limit is refused

Priority: P1 · Covers: FR-TEN-02, FR-LIC-02 · Automation: Automated (API, partial)

Preconditions: baseline. OWNER_B is on the Free trial (1 garage) and owns B1.

Test data: OWNER_B; garage name QA Gara B2 Cau Giay.

Steps

  1. Sign in as [email protected] → Garages → Add garage with the test data.

Expected results

  • Refused with “Your plan limit is reached. Upgrade to add more.” and a link to Subscription.
  • No garage QA Gara B2 Cau Giay exists afterwards.

E2E-TEN-005 · Switch the active garage

Priority: P1 · Covers: FR-TEN-03 · Automation: Automated (API, partial)

Preconditions: baseline. Signed in as OWNER_A.

Steps

  1. In the header selector, choose QA Gara A1 Quan 1 and open Customers.
  2. Choose QA Gara A2 Thu Duc in the selector, staying on Customers.
  3. Reload the page.

Expected results

  • Step 1 lists An Nguyen and Binh Tran only.
  • Step 2 lists Chi Le only, without signing in again.
  • After the reload, A2 is still the active garage.

E2E-TEN-006 · Deactivate and reactivate a garage

Priority: P1 · Covers: FR-TEN-02 · Automation: Automated (API, partial)

Preconditions: baseline. OWNER_A in one browser, ADVISOR_A1 in another, both with A1 active.

Steps

  1. OWNER_A: settings of QA Gara A1 Quan 1 → Status → Deactivate, and confirm.
  2. ADVISOR_A1: reload Customers.
  3. OWNER_A: open Customers of A1.
  4. OWNER_A: Reactivate A1 and confirm.
  5. ADVISOR_A1: reload Customers.

Expected results

  • Step 1 asks “Deactivate QA Gara A1 Quan 1? Staff lose access until you reactivate it.” After confirming, Garages shows A1 as Inactive.
  • Step 2: the advisor no longer sees A1’s data; the page says “This garage is inactive.”
  • Step 3: the owner still reaches the garage (to reactivate it) and sees “This garage is inactive.”
  • Step 4 asks “Reactivate QA Gara A1 Quan 1? Its staff regain access.” and A1 is Active again.
  • Step 5: the advisor sees A1’s customers again.

Priority: P1 · Covers: FR-TEN-04, FR-NOT-04 · Automation: Automated (API, partial)

Preconditions: baseline. FREELANCER is not in A2 and has no pending invitation.

Test data: inviter OWNER_A, garage QA Gara A2 Thu Duc, invitee [email protected], group Service Advisor.

Steps

  1. OWNER_A, A2 active → Staff → Invite staff.
  2. Enter [email protected] and press Find.
  3. Tick Service Advisor and press Send invitation.
  4. In Mailpit, open the invitation e-mail to [email protected] and follow its link.
  5. Sign in as [email protected] if asked. Press Accept.
  6. Open the header garage selector, choose A2, open Customers.
  7. OWNER_A: reload Staff in A2.

Expected results

  • Step 2 shows the found user: QA Freelancer, its e-mail and phone.
  • After step 3 the invitation is listed under Invitations: QA Freelancer, Service Advisor, Pending, expiry date 7 days ahead, with Revoke.
  • The e-mail is in FREELANCER’s language, names the inviter QA Owner A and the garage QA Gara A2 Thu Duc, and links to /invitations/<id> on the owner web.
  • Before accepting, FREELANCER sees a badge on Invitations and a dot on the bell. The page reads “QA Owner A invited you to join QA Gara A2 Thu Duc”.
  • After Accept: “You joined QA Gara A2 Thu Duc.”; the badge disappears; A2 appears in the selector with the Staff tag.
  • FREELANCER sees A2’s customers (Chi Le) and can add a customer (Service Advisor has customers write).
  • OWNER_A sees QA Freelancer under Members with group Service Advisor and status Active; the invitation shows Accepted.
  • Clean-up: OWNER_A removes QA Freelancer from A2 (see E2E-TEN-015 step 5).

E2E-TEN-008 · Invite a user by phone number

Priority: P2 · Covers: FR-TEN-04 · Automation: Automated (API, partial)

Preconditions: baseline. FREELANCER is not in A1.

Test data: lookup 0900000301 (FREELANCER’s phone), group Technician, garage A1.

Steps

  1. OWNER_A, A1 active → Invite staff → enter 0900000301 → Find.
  2. Tick Technician and send.
  3. Sign in as FREELANCER, open Invitations and accept.

Expected results

  • The lookup finds QA Freelancer by phone.
  • The invitation e-mail goes to [email protected].
  • After accepting, FREELANCER is a member of A1 with group Technician: the sidebar shows no Customers, Suppliers or Staff.
  • Clean-up: OWNER_A removes QA Freelancer from A1.

E2E-TEN-009 · Invitation lookups that cannot be invited

Priority: P2 · Covers: FR-TEN-04 · Automation: Automated (API, partial)

Preconditions: baseline. Signed in as OWNER_A with A1 active. FREELANCER has a pending invitation to A1 (send one before step 3; revoke it at the end).

Test data:

CaseLookup
a. No such user[email protected]
b. Already a member[email protected]
c. Already invited[email protected]
d. The owner[email protected]

Steps

  1. For each case, open Invite staff, enter the lookup and press Find.

Expected results

  • a. “No VietGara user uses this e-mail or phone.” Nothing else is offered: only existing users can be invited.
  • b. The user is shown with the notice “This user is already a member of the garage.” instead of the group list and send button.
  • c. The notice “This user already has a pending invitation.”
  • d. The notice that the user is already a member (the owner is a member of every garage they own).

E2E-TEN-010 · Decline an invitation

Priority: P2 · Covers: FR-TEN-04 · Automation: Automated (API, partial)

Preconditions: baseline. OWNER_A has invited FREELANCER to A1 (group Technician).

Steps

  1. Sign in as FREELANCER → Invitations → Decline.
  2. OWNER_A: reload Staff in A1.
  3. OWNER_A: invite FREELANCER to A1 again.

Expected results

  • The invitation leaves FREELANCER’s pending list; A1 is not in their garage selector.
  • OWNER_A sees the invitation as Declined and no new member.
  • Step 3 is allowed: a declined invitation does not block a new one. (Revoke it afterwards.)

E2E-TEN-011 · Revoke a pending invitation

Priority: P2 · Covers: FR-TEN-04 · Automation: Automated (API, partial)

Preconditions: baseline. OWNER_A has invited FREELANCER to A1. FREELANCER has the invitation e-mail open in Mailpit.

Steps

  1. OWNER_A → Staff (A1) → Revoke on the pending invitation.
  2. FREELANCER follows the e-mail link and presses Accept (or opens Invitations).

Expected results

  • The invitation shows Revoked for the owner.
  • FREELANCER can no longer accept: the invitation is gone from the pending list, or accepting shows “The invitation was already answered or revoked.” A1 is not in their selector.
  • Subscription of OWNER_A: the staff count no longer includes this invitation.

E2E-TEN-012 · An expired invitation cannot be accepted

Priority: P3 · Covers: FR-TEN-04 · Automation: Automated (API, partial)

Preconditions: baseline. OWNER_A has invited FREELANCER to A1. Apply SQL helper H4 for [email protected] (README §3.4).

Steps

  1. FREELANCER opens the invitation link from the e-mail and presses Accept.
  2. OWNER_A reloads Staff (A1).

Expected results

  • Accepting is refused with “The invitation expired.”; FREELANCER does not join A1.
  • The owner sees the invitation as Expired (or no longer pending) and can invite FREELANCER again.

Priority: P3 · Covers: FR-TEN-04 · Automation: Automated (API, partial)

Preconditions: baseline. OWNER_A has invited FREELANCER to A1; copy the invitation link from Mailpit.

Steps

  1. Sign in as [email protected] and open the copied link.

Expected results

  • The page shows “Invitation not found, or it is not addressed to this user.” with no Accept button.
  • FREELANCER’s invitation is still Pending (check as OWNER_A). Revoke it afterwards.

E2E-TEN-014 · Change a member’s groups; permissions change at once

Priority: P1 · Covers: FR-TEN-04, FR-IAM-03 · Automation: Automated (API, partial)

Preconditions: baseline. OWNER_A and VIEWER_A1 signed in in two browsers, A1 active.

Test data: VIEWER_A1 (group QA Customer Viewers: customers read only).

Steps

  1. VIEWER_A1 opens Customers.
  2. OWNER_A → Staff (A1) → QA Customer Viewer A1 → Change groups: add Service Advisor (keep QA Customer Viewers). Save.
  3. VIEWER_A1 reloads Customers.
  4. OWNER_A removes Service Advisor again.
  5. VIEWER_A1 reloads Customers.

Expected results

  • Step 1: customers are listed, but there is no New customer button and no edit or delete actions. Suppliers and Staff are not in the sidebar.
  • Step 3, without signing out: New customer and edit actions appear, and Suppliers appears (Service Advisor can view suppliers). The member holds the union of both groups.
  • Step 5: back to read-only customers and no Suppliers.

E2E-TEN-015 · Deactivate, reactivate and remove a member

Priority: P1 · Covers: FR-TEN-04 · Automation: Automated (API, partial)

Preconditions: baseline. FREELANCER is a member of A2 with group Service Advisor (run E2E-TEN-007 steps 1–5 first). OWNER_A and FREELANCER signed in in two browsers.

Steps

  1. OWNER_A → Staff (A2) → QA Freelancer → Deactivate.
  2. FREELANCER selects A2 and opens Customers.
  3. OWNER_A → Activate QA Freelancer.
  4. FREELANCER reloads Customers.
  5. OWNER_A → Remove from garage → confirm.
  6. FREELANCER reloads the page and opens the garage selector.
  7. OWNER_A looks at the owner’s own row in the member list.

Expected results

  • Step 1: QA Freelancer shows Inactive. Step 2: FREELANCER has no access to A2’s data.
  • Step 4: access is back.
  • Step 5 asks “Remove QA Freelancer from the garage? They lose access at once.”
  • Step 6: A2 is no longer in FREELANCER’s selector.
  • Step 7: the owner row has no actions (no change groups, deactivate or remove).

E2E-TEN-016 · A manager can manage staff but not roles or the subscription

Priority: P2 · Covers: FR-TEN-04, FR-IAM-03, FR-LIC-02 · Automation: Automated (API, partial)

Preconditions: baseline. Signed in as MANAGER_A1, A1 active.

Steps

  1. Open Staff, invite FREELANCER with group Technician. Then revoke the invitation.
  2. Open settings of A1 and change the phone to 0283000111, save; change it back.
  3. Open Roles & groups (type /roles if it is not in the menu).
  4. Open Subscription.
  5. In the settings of A1, look for the Status card.

Expected results

  • Steps 1 and 2 work (Manager has staff and garage write).
  • Step 3 shows “Only garage owners manage roles and groups.” and no editing.
  • Step 4 shows the plan but no plan change: “Only the user owner manages the subscription.”
  • Step 5: no deactivate button: only the owner can change the garage status.

E2E-TEN-017 · Create a custom role in the role editor

Priority: P1 · Covers: FR-IAM-03 · Automation: Automated (API, partial)

Preconditions: baseline. Signed in as OWNER_A.

Test data: role QA Supplier Clerk, description Manages suppliers only, permissions Suppliers → Add & edit.

Steps

  1. Roles & groups → Add role.
  2. Enter the name and description.
  3. Tick Suppliers → Add & edit.
  4. Untick Suppliers → View.
  5. Tick Suppliers → Add & edit again, then Save.
  6. Go back to Roles & groups.

Expected results

  • Step 3 ticks Suppliers → View too (write includes read); the save bar shows “2 unsaved changes” (or the count of changed cells).
  • Step 4 also unticks Add & edit.
  • Rows for quotes, repair orders, inventory, billing and reports are muted with “Coming soon” but can be ticked.
  • A cell with no such action (e.g. Garage → View, Reports → Add & edit) shows ”—”.
  • Save shows “Role created.” and moves to the new role’s editor; the impact line says “No staff group uses this role yet.”
  • Roles & groups lists QA Supplier Clerk without the Default tag, with its permission count and “Used by 0 groups”.
  • Clean-up: delete QA Supplier Clerk.

E2E-TEN-018 · Role editor: unsaved changes, undo and reset to default

Priority: P2 · Covers: FR-IAM-03 · Automation: Automated (API, partial)

Preconditions: baseline. Signed in as OWNER_A.

Test data: default role Technician.

Steps

  1. Open the editor of Technician. Look at Reset to default and the impact line.
  2. Tick Customers & vehicles → View. Press Undo changes.
  3. Tick Customers & vehicles → View again, then pick Accountant in the role selector.
  4. In the dialog, press Cancel; then pick Accountant again and press Discard.
  5. Open Technician again, tick Customers & vehicles → View, press Save.
  6. Press Reset to default, then Save.
  7. With unsaved changes, reload the browser tab.

Expected results

  • Step 1: Reset to default is disabled (the grid matches the defaults). The impact line says the role applies to the members of the groups using it (Technician is used by the Technician groups of A1 and A2).
  • Step 2: the grid returns to its saved state; “No unsaved changes”.
  • Step 3 opens “Discard unsaved changes?”.
  • Step 4: Cancel keeps the changes on Technician; Discard opens Accountant’s editor.
  • Step 5: “Changes saved.”; TECH_A1A2 can now see Customers in A1 and A2 (check in their browser).
  • Step 6 restores the default permissions (Customers View unticked) and saves; TECH_A1A2 loses Customers again. The name and description are unchanged.
  • Step 7: the browser shows its own “leave page” warning.

E2E-TEN-019 · Role and group names must be unique; a role in use cannot be deleted

Priority: P2 · Covers: FR-IAM-03 · Automation: Automated (API, partial)

Preconditions: baseline. Signed in as OWNER_A, A1 active.

Steps

  1. Add role named qa customer viewer (lower case) with Customers → View. Save.
  2. Delete the role QA Customer Viewer.
  3. Add group in A1 named QA CUSTOMER VIEWERS with role Technician.

Expected results

  • Step 1: “A role with this name exists.” (names are compared ignoring letter case).
  • Step 2: “Groups still use this role.” The role stays.
  • Step 3: “A group with this name exists in the garage.”

E2E-TEN-020 · Staff groups of a garage

Priority: P2 · Covers: FR-IAM-03 · Automation: Automated (API, partial)

Preconditions: baseline. Signed in as OWNER_A.

Test data: group QA Night Shift in A2, role Technician.

Steps

  1. Active garage A2 → Roles & groups → Add group QA Night Shift with role Technician.
  2. Switch to A1 and look at the group list.
  3. Switch back to A2; edit QA Night Shift and change its role to Service Advisor.
  4. Delete QA Night Shift.

Expected results

  • Step 1: the group appears in “Groups in QA Gara A2 Thu Duc” with role Technician and 0 members.
  • Step 2: A1 does not have QA Night Shift: groups belong to one garage.
  • Step 3 saves the new role.
  • Step 4 asks “Delete the group QA Night Shift? Members keep their other groups.” and removes it.

E2E-TEN-021 · Staff in two garages has separate permissions in each

Priority: P2 · Covers: FR-TEN-03, FR-IAM-03 · Automation: Automated (API, partial)

Preconditions: baseline. TECH_A1A2 is Technician in A1 and A2.

Steps

  1. OWNER_A → Staff (A2) → QA Technician A1-A2 → Change groups: add Service Advisor.
  2. Sign in as [email protected]. Select A2 and look at the sidebar.
  3. Select A1 and look at the sidebar.
  4. OWNER_A removes Service Advisor from the member in A2 again.

Expected results

  • In A2 the sidebar shows Customers, Vehicles and Suppliers.
  • In A1 it does not: permissions are per garage.
  • Subscription of OWNER_A still counts TECH_A1A2 once in Staff.

E2E-TEN-022 · One owner cannot reach another owner’s garage

Priority: P1 · Covers: FR-TEN-02, NFR security (tenant isolation) · Automation: Automated (API, partial)

Preconditions: baseline. As OWNER_A, open the settings of A1 and copy the address (/garages/<A1 id>/settings); open customer An Nguyen and copy the address (/customers/<id>).

Steps

  1. Sign in as [email protected]. Open the garage selector.
  2. Paste the A1 settings address.
  3. Paste An Nguyen’s address.
  4. Open Customers of B1 and search 0911000001.

Expected results

  • Step 1: only QA Gara B1 Ba Dinh.
  • Steps 2 and 3 show no A1 data: “You have no access to this garage.” or “Not found.”
  • Step 4 finds only QA Customer Dung Pham; An Nguyen (same phone, other tenant) never appears.

E2E-TEN-023 · Document numbering template validation

Priority: P3 · Covers: FR-TEN-05 · Automation: Automated (API, partial)

Preconditions: baseline. Signed in as OWNER_A, settings of A1.

Test data:

CaseQuote number templateExpected
aBG-{YYYY}{MM}-{SEQ:4}Saved
bBG-{YYYY}{MM} (no sequence)Refused
cBG-{SEQ:4}-{SEQ:2} (two sequences)Refused

Steps

  1. For each case, enter the template in Quote number template on the Configuration card and save.

Expected results

  • The field hint reads “Use {YYYY}, {YY}, {MM}, {DD} and exactly one {SEQ:n}, e.g. BG-{YYYY}{MM}-{SEQ:4}.”
  • a is saved (“Changes saved.”).
  • b and c are refused, with the template field highlighted; the stored value stays that of case a.