feat(api): add M5 coverage controls and frontend spec
This commit is contained in:
@@ -37,6 +37,7 @@ async function apiCall(path, {
|
||||
idempotencyKey,
|
||||
body,
|
||||
expectedStatus = 200,
|
||||
allowFailure = false,
|
||||
} = {}) {
|
||||
const headers = {};
|
||||
if (token) headers.Authorization = `Bearer ${token}`;
|
||||
@@ -49,6 +50,12 @@ async function apiCall(path, {
|
||||
body: body === undefined ? undefined : JSON.stringify(body),
|
||||
});
|
||||
const payload = await readJson(response);
|
||||
if (allowFailure) {
|
||||
return {
|
||||
statusCode: response.status,
|
||||
body: payload,
|
||||
};
|
||||
}
|
||||
if (response.status !== expectedStatus) {
|
||||
throw new Error(`${method} ${path} expected ${expectedStatus}, got ${response.status}: ${JSON.stringify(payload)}`);
|
||||
}
|
||||
@@ -381,6 +388,29 @@ async function main() {
|
||||
assert.ok(Array.isArray(teamMembers.items));
|
||||
logStep('client.team-members.ok', { count: teamMembers.items.length });
|
||||
|
||||
const createdShiftManager = await apiCall('/client/shift-managers', {
|
||||
method: 'POST',
|
||||
token: ownerSession.sessionToken,
|
||||
idempotencyKey: uniqueKey('create-shift-manager'),
|
||||
body: {
|
||||
hubId: fixture.clockPoint.id,
|
||||
email: `smoke.manager.${Date.now()}@krowd.com`,
|
||||
firstName: 'Smoke',
|
||||
lastName: 'Manager',
|
||||
phone: '+15550009999',
|
||||
},
|
||||
});
|
||||
assert.ok(createdShiftManager.businessMembershipId);
|
||||
assert.equal(createdShiftManager.membershipStatus, 'INVITED');
|
||||
assert.ok(createdShiftManager.managerAssignmentId);
|
||||
logStep('client.shift-manager.create.ok', createdShiftManager);
|
||||
|
||||
const teamMembersAfterCreate = await apiCall('/client/team-members', {
|
||||
token: ownerSession.sessionToken,
|
||||
});
|
||||
assert.ok(teamMembersAfterCreate.items.some((item) => item.businessMembershipId === createdShiftManager.businessMembershipId));
|
||||
logStep('client.team-members.after-create.ok', { count: teamMembersAfterCreate.items.length });
|
||||
|
||||
const viewedOrders = await apiCall(`/client/orders/view?${reportWindow}`, {
|
||||
token: ownerSession.sessionToken,
|
||||
});
|
||||
@@ -754,7 +784,9 @@ async function main() {
|
||||
});
|
||||
const openShift = openShifts.items.find((shift) => shift.shiftId === fixture.shifts.available.id)
|
||||
|| openShifts.items[0];
|
||||
const blockedApplyCandidate = openShifts.items.find((shift) => shift.shiftId !== openShift.shiftId);
|
||||
assert.ok(openShift);
|
||||
assert.ok(blockedApplyCandidate);
|
||||
logStep('staff.shifts.open.ok', { count: openShifts.items.length });
|
||||
|
||||
const pendingShifts = await apiCall('/staff/shifts/pending', {
|
||||
@@ -858,6 +890,13 @@ async function main() {
|
||||
assert.ok(Array.isArray(benefits.items));
|
||||
logStep('staff.profile.benefits.ok', { count: benefits.items.length });
|
||||
|
||||
const benefitHistory = await apiCall('/staff/profile/benefits/history?limit=10', {
|
||||
token: staffAuth.idToken,
|
||||
});
|
||||
assert.ok(Array.isArray(benefitHistory.items));
|
||||
assert.ok(benefitHistory.items.length >= 1);
|
||||
logStep('staff.profile.benefits.history.ok', { count: benefitHistory.items.length });
|
||||
|
||||
const timeCard = await apiCall(`/staff/profile/time-card?month=${new Date().getUTCMonth() + 1}&year=${new Date().getUTCFullYear()}`, {
|
||||
token: staffAuth.idToken,
|
||||
});
|
||||
@@ -1168,6 +1207,59 @@ async function main() {
|
||||
});
|
||||
logStep('staff.shifts.request-swap.ok', requestedSwap);
|
||||
|
||||
const blockedReview = await apiCall('/client/coverage/reviews', {
|
||||
method: 'POST',
|
||||
token: ownerSession.sessionToken,
|
||||
idempotencyKey: uniqueKey('coverage-block'),
|
||||
body: {
|
||||
staffId: fixture.staff.ana.id,
|
||||
assignmentId: fixture.assignments.completedAna.id,
|
||||
rating: 2,
|
||||
markAsBlocked: true,
|
||||
markAsFavorite: false,
|
||||
issueFlags: ['LATE_CLOCK_IN'],
|
||||
feedback: 'Smoke blocked staff test',
|
||||
},
|
||||
});
|
||||
assert.equal(blockedReview.markAsBlocked, true);
|
||||
logStep('client.coverage.block.ok', blockedReview);
|
||||
|
||||
const blockedStaff = await apiCall('/client/coverage/blocked-staff', {
|
||||
token: ownerSession.sessionToken,
|
||||
});
|
||||
assert.ok(blockedStaff.items.some((item) => item.staffId === fixture.staff.ana.id));
|
||||
logStep('client.coverage.blocked-staff.ok', { count: blockedStaff.items.length });
|
||||
|
||||
const blockedApplyAttempt = await apiCall(`/staff/shifts/${blockedApplyCandidate.shiftId}/apply`, {
|
||||
method: 'POST',
|
||||
token: staffAuth.idToken,
|
||||
idempotencyKey: uniqueKey('staff-shift-apply-blocked'),
|
||||
body: {
|
||||
roleId: blockedApplyCandidate.roleId,
|
||||
},
|
||||
allowFailure: true,
|
||||
});
|
||||
assert.equal(blockedApplyAttempt.statusCode, 409);
|
||||
assert.equal(blockedApplyAttempt.body?.code, 'STAFF_BLOCKED');
|
||||
logStep('staff.shifts.apply-blocked.ok', blockedApplyAttempt.body);
|
||||
|
||||
const unblockedReview = await apiCall('/client/coverage/reviews', {
|
||||
method: 'POST',
|
||||
token: ownerSession.sessionToken,
|
||||
idempotencyKey: uniqueKey('coverage-unblock'),
|
||||
body: {
|
||||
staffId: fixture.staff.ana.id,
|
||||
assignmentId: fixture.assignments.completedAna.id,
|
||||
rating: 5,
|
||||
markAsBlocked: false,
|
||||
markAsFavorite: true,
|
||||
issueFlags: [],
|
||||
feedback: 'Smoke unblock cleanup',
|
||||
},
|
||||
});
|
||||
assert.equal(unblockedReview.markAsBlocked, false);
|
||||
logStep('client.coverage.unblock.ok', unblockedReview);
|
||||
|
||||
const uploadedProfilePhoto = await uploadFile('/staff/profile/photo', staffAuth.idToken, {
|
||||
filename: 'profile-photo.jpg',
|
||||
contentType: 'image/jpeg',
|
||||
|
||||
Reference in New Issue
Block a user