@instructure/platform-mock-canvas-api 0.4.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,7 @@
1
1
  export { courses } from './courses';
2
2
  export { educatorAccessibilityCourseStatistics, mockEducatorAccessibilityStatistic, } from './educatorAccessibility';
3
+ export type { CanvasCompletionRequirement, CanvasContentDetails, CanvasCourseModules, CanvasModule, CanvasModuleItem, CanvasModuleItemType, } from './modules';
4
+ export { buildCanvasCourseModules, MODULE_ITEMS_INLINE_LIMIT, MODULES_SMALL_MODULE_COUNT, moduleScenariosByCourseId, modulesCourseEmpty, modulesCourseFatModule, modulesCourseLarge, modulesCourseSmall, } from './modules';
3
5
  export { stickerDescriptions } from './stickerDescriptions';
4
6
  export { stickerTranslations } from './stickerTranslations';
5
7
  export { submissions, submissionsStore } from './submissions';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/fixtures/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EACL,qCAAqC,EACrC,kCAAkC,GACnC,MAAM,yBAAyB,CAAA;AAChC,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAA;AAC3D,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAA;AAC3D,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/fixtures/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EACL,qCAAqC,EACrC,kCAAkC,GACnC,MAAM,yBAAyB,CAAA;AAChC,YAAY,EACV,2BAA2B,EAC3B,oBAAoB,EACpB,mBAAmB,EACnB,YAAY,EACZ,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,WAAW,CAAA;AAClB,OAAO,EACL,wBAAwB,EACxB,yBAAyB,EACzB,0BAA0B,EAC1B,yBAAyB,EACzB,kBAAkB,EAClB,sBAAsB,EACtB,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,WAAW,CAAA;AAClB,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAA;AAC3D,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAA;AAC3D,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA"}
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Canvas REST wire shapes for the modules API.
3
+ *
4
+ * These are deliberately *not* the normalised domain model that
5
+ * `@instructure/platform-modules` exposes — they are what
6
+ * `lib/api/v1/context_module.rb` actually puts on the wire: snake_case keys,
7
+ * numeric ids, CamelCase item types, `quiz_lti` for new quizzes, and an
8
+ * `estimated_duration` that Canvas Career serialises as an ISO-8601 duration.
9
+ * A REST adapter that can turn these into the domain model has been proven
10
+ * against the real shape rather than a tidied-up one.
11
+ *
12
+ * The four scenarios are sized to match the ones in
13
+ * `packages/modules/src/fixtures/scenarios.ts`, so a REST run and an in-memory
14
+ * run are comparable. Generation is deterministic: no `Math.random()`, no
15
+ * `Date.now()`.
16
+ *
17
+ * The generation *rules* are deliberately not identical, and it would be wrong to
18
+ * claim they are: these produce Canvas's wire shape, so the student state arrives
19
+ * as a module-level `state` string plus a per-item `completion_requirement.completed`
20
+ * flag, where the domain fixtures carry an already-derived `ModuleStatus` and
21
+ * `ModuleItemStatus`. Turning the former into the latter is exactly the job a REST
22
+ * adapter has to do, so the two must not agree too early.
23
+ *
24
+ * These fixtures cannot be imported from `@instructure/platform-modules` — that
25
+ * package depends on this one — so the shared numbers are restated here with the
26
+ * reason attached rather than derived.
27
+ */
28
+ /** Canvas's item `type` values, as sent. */
29
+ export type CanvasModuleItemType = 'Assignment' | 'Quiz' | 'Discussion' | 'Page' | 'File' | 'ExternalUrl' | 'ExternalTool' | 'SubHeader';
30
+ export interface CanvasCompletionRequirement {
31
+ type: string;
32
+ /** Sent only for `min_score` (`lib/api/v1/context_module.rb:166`). */
33
+ min_score?: number;
34
+ /** Sent only for `min_percentage` (`lib/api/v1/context_module.rb:167`). */
35
+ min_percentage?: number;
36
+ /** Per-student: whether this requirement is in `progression.requirements_met`. */
37
+ completed: boolean;
38
+ }
39
+ export interface CanvasContentDetails {
40
+ points_possible?: number;
41
+ due_at?: string | null;
42
+ unlock_at?: string | null;
43
+ lock_at?: string | null;
44
+ }
45
+ export interface CanvasModuleItem {
46
+ id: number;
47
+ module_id: number;
48
+ position: number;
49
+ title: string;
50
+ indent: number;
51
+ type: CanvasModuleItemType;
52
+ content_id?: number;
53
+ html_url: string;
54
+ url?: string;
55
+ page_url?: string;
56
+ external_url?: string;
57
+ new_tab?: boolean;
58
+ published: boolean;
59
+ /** Set on new quizzes, which Canvas reports as `Assignment`. */
60
+ quiz_lti?: boolean;
61
+ completion_requirement?: CanvasCompletionRequirement;
62
+ content_details?: CanvasContentDetails;
63
+ /** Canvas Career only. ISO-8601 duration, e.g. `PT25M`. */
64
+ estimated_duration?: string;
65
+ }
66
+ export interface CanvasModule {
67
+ id: number;
68
+ name: string;
69
+ position: number;
70
+ unlock_at: string | null;
71
+ require_sequential_progress: boolean;
72
+ prerequisite_module_ids: number[];
73
+ published: boolean;
74
+ items_count: number;
75
+ items_url: string;
76
+ /**
77
+ * Present only when the module has at most `MODULE_ITEMS_INLINE_LIMIT`
78
+ * items. Above that Canvas omits the key entirely — see the note on the
79
+ * constant.
80
+ */
81
+ items?: CanvasModuleItem[];
82
+ state: string;
83
+ completed_at: string | null;
84
+ publish_final_grade: boolean;
85
+ }
86
+ export interface CanvasCourseModules {
87
+ courseId: number;
88
+ modules: CanvasModule[];
89
+ /** Every module's items, including the ones too large to be inlined. */
90
+ itemsByModuleId: Record<string, CanvasModuleItem[]>;
91
+ }
92
+ /**
93
+ * Canvas refuses to inline items for a module with more than this many
94
+ * (`Api::MAX_PER_PAGE`, via `lib/api/v1/context_module.rb`): the `items` key is
95
+ * absent from the response rather than truncated, and the client is expected
96
+ * to fall back to `items_url`. Mirrors `INLINE_ITEM_LIMIT` in
97
+ * `@instructure/platform-modules`.
98
+ */
99
+ export declare const MODULE_ITEMS_INLINE_LIMIT = 100;
100
+ /** Build a course's modules from a per-module item count, e.g. `[8, 250, 8]`. */
101
+ export declare function buildCanvasCourseModules(courseId: number, itemCounts: readonly number[]): CanvasCourseModules;
102
+ /**
103
+ * 7 modules × 8 items. Everything inlines; the course loads in ~2 requests.
104
+ *
105
+ * Seven, matching `courseSmall` in `packages/modules` — sized to `MODULE_STATUS_CYCLE.length`
106
+ * so no `ModuleStatus` is silently omitted.
107
+ */
108
+ export declare const MODULES_SMALL_MODULE_COUNT = 7;
109
+ export declare function modulesCourseSmall(): CanvasCourseModules;
110
+ /** 120 modules × 25 items. Forces module-list pagination past `per_page=100`. */
111
+ export declare function modulesCourseLarge(): CanvasCourseModules;
112
+ /** 3 modules, the middle one 250 items — that module ships with no `items` key. */
113
+ export declare function modulesCourseFatModule(): CanvasCourseModules;
114
+ /** No modules. The empty state, on the wire. */
115
+ export declare function modulesCourseEmpty(): CanvasCourseModules;
116
+ /**
117
+ * Which scenario each course id serves. The ids line up with the `courses`
118
+ * fixture, so `GET /api/v1/courses/2/modules` is the 120-module course.
119
+ *
120
+ * Builders rather than values: `modulesCourseLarge()` is 3,000 item objects
121
+ * and nothing should pay for it at import time.
122
+ */
123
+ export declare const moduleScenariosByCourseId: Record<string, () => CanvasCourseModules>;
124
+ //# sourceMappingURL=modules.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"modules.d.ts","sourceRoot":"","sources":["../../src/fixtures/modules.ts"],"names":[],"mappings":"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"}
package/dist/fixtures.js CHANGED
@@ -1,4 +1,4 @@
1
- const i = [
1
+ const I = [
2
2
  {
3
3
  id: "1",
4
4
  uuid: "WvAU2cWNVZjm0Dup4i36bSusZ4UReXfNVhBjxooC",
@@ -39,7 +39,7 @@ const i = [
39
39
  enrollment_term_id: "2",
40
40
  term: { id: "2", name: "Spring 2025" }
41
41
  }
42
- ], a = (e = {}) => ({
42
+ ], D = (s = {}) => ({
43
43
  id: 1,
44
44
  course_id: 101,
45
45
  course_name: "Introduction to Biology",
@@ -51,8 +51,8 @@ const i = [
51
51
  workflow_state: "active",
52
52
  created_at: "2026-01-01T00:00:00Z",
53
53
  updated_at: "2026-01-02T00:00:00Z",
54
- ...e
55
- }), c = [
54
+ ...s
55
+ }), L = [
56
56
  {
57
57
  id: 1,
58
58
  course_id: 101,
@@ -92,7 +92,143 @@ const i = [
92
92
  course_code: "CHM301",
93
93
  published: !1
94
94
  }
95
- ], r = {
95
+ ], k = 100, u = [
96
+ "assignment",
97
+ "quiz",
98
+ "discussion",
99
+ "page",
100
+ "file",
101
+ "external_url",
102
+ "external_tool",
103
+ "sub_header"
104
+ ], _ = [
105
+ "must_view",
106
+ "must_submit",
107
+ "must_contribute",
108
+ "must_mark_done",
109
+ "min_score",
110
+ "min_percentage"
111
+ ], f = ["assignment", "quiz", "discussion"], g = Date.UTC(2025, 0, 6), w = 864e5, l = (s) => new Date(g + s * w).toISOString(), m = ["unlocked", "started", "completed", "locked"], h = (s) => m[(s - 1) % m.length], A = (s, e) => {
112
+ const i = _[Math.floor(s / 3) % _.length], a = h(e), t = a === "completed" || a === "started" && s % 2 === 0;
113
+ return i === "min_score" ? { type: i, min_score: 10, completed: t } : i === "min_percentage" ? { type: i, min_percentage: 80, completed: t } : { type: i, completed: t };
114
+ }, b = (s, e, i, a) => {
115
+ const t = i + 5e4;
116
+ switch (s) {
117
+ case "assignment":
118
+ return {
119
+ type: "Assignment",
120
+ content_id: t,
121
+ url: `/api/v1/courses/${e}/assignments/${t}`
122
+ };
123
+ case "quiz":
124
+ return Math.floor(a / u.length) % 2 === 0 ? {
125
+ type: "Quiz",
126
+ content_id: t,
127
+ url: `/api/v1/courses/${e}/quizzes/${t}`
128
+ } : {
129
+ type: "Assignment",
130
+ quiz_lti: !0,
131
+ content_id: t,
132
+ url: `/api/v1/courses/${e}/assignments/${t}`
133
+ };
134
+ case "discussion":
135
+ return {
136
+ type: "Discussion",
137
+ content_id: t,
138
+ url: `/api/v1/courses/${e}/discussion_topics/${t}`
139
+ };
140
+ case "page":
141
+ return {
142
+ type: "Page",
143
+ page_url: `page-${i}`,
144
+ url: `/api/v1/courses/${e}/pages/page-${i}`
145
+ };
146
+ case "file":
147
+ return { type: "File", content_id: t, url: `/api/v1/files/${t}` };
148
+ case "external_url":
149
+ return {
150
+ type: "ExternalUrl",
151
+ external_url: `https://example.com/resource/${i}`,
152
+ new_tab: !0
153
+ };
154
+ case "external_tool":
155
+ return {
156
+ type: "ExternalTool",
157
+ content_id: t,
158
+ external_url: `https://lti.example.com/launch/${i}`,
159
+ new_tab: !1
160
+ };
161
+ case "sub_header":
162
+ return { type: "SubHeader" };
163
+ }
164
+ }, y = (s, e, i, a) => {
165
+ const t = u[a % u.length], o = e * 1e3 + i + 1, n = t === "sub_header", r = {
166
+ id: o,
167
+ module_id: e,
168
+ position: i + 1,
169
+ title: `${t} ${i + 1}`,
170
+ indent: i % 5 === 4 ? 1 : 0,
171
+ html_url: `/courses/${s}/modules/items/${o}`,
172
+ published: a % 7 !== 0,
173
+ ...b(t, s, o, a)
174
+ };
175
+ return a % 3 === 0 && !n && (r.completion_requirement = A(a, e)), f.includes(t) && a % 4 === 0 && (r.content_details = { points_possible: 10 + a % 5 * 5, due_at: l(a % 90) }), a % 37 === 0 && (r.estimated_duration = `PT${5 + a % 4 * 10}M`), r;
176
+ }, S = (s, e, i) => {
177
+ const a = h(e), t = {
178
+ id: e,
179
+ name: `Module ${e}`,
180
+ position: e,
181
+ unlock_at: e % 5 === 0 ? l(e) : null,
182
+ require_sequential_progress: e % 6 === 0,
183
+ prerequisite_module_ids: e > 1 && e % 3 === 0 ? [e - 1] : [],
184
+ published: e % 4 !== 0,
185
+ // `items_count` is the true total, whether or not the items are inlined.
186
+ items_count: i.length,
187
+ items_url: `/api/v1/courses/${s}/modules/${e}/items`,
188
+ state: a,
189
+ // Canvas sets `completed_at` only on a completed progression
190
+ // (`app/models/context_module_progression.rb:57-61`).
191
+ completed_at: a === "completed" ? l(e + 30) : null,
192
+ publish_final_grade: !1
193
+ };
194
+ return i.length <= k && (t.items = i), t;
195
+ };
196
+ function c(s, e) {
197
+ const i = [], a = {};
198
+ let t = 0;
199
+ return e.forEach((o, n) => {
200
+ const r = n + 1, p = Array.from(
201
+ { length: o },
202
+ (Z, d) => y(s, r, d, t + d)
203
+ );
204
+ t += o, i.push(S(s, r, p)), a[String(r)] = p;
205
+ }), { courseId: s, modules: i, itemsByModuleId: a };
206
+ }
207
+ const T = 7;
208
+ function v() {
209
+ return c(
210
+ 1,
211
+ Array.from({ length: T }, () => 8)
212
+ );
213
+ }
214
+ function E() {
215
+ return c(
216
+ 2,
217
+ Array.from({ length: 120 }, () => 25)
218
+ );
219
+ }
220
+ function C() {
221
+ return c(3, [8, 250, 8]);
222
+ }
223
+ function M() {
224
+ return c(4, []);
225
+ }
226
+ const O = {
227
+ 1: v,
228
+ 2: E,
229
+ 3: C,
230
+ 4: M
231
+ }, B = {
96
232
  default: "A placeholder sticker with a smiley face and a plus sign.",
97
233
  star: "A sticker with a picture of a smiling star.",
98
234
  trophy: "A sticker with a picture of a golden trophy.",
@@ -144,13 +280,13 @@ const i = [
144
280
  tag: "A sticker with a picture of a clip-on identification tag.",
145
281
  presentation: "A sticker with a picture of a projector screen with presentation content on it.",
146
282
  column: "A sticker with a picture of a roman column."
147
- }, o = {
283
+ }, x = {
148
284
  chooseASticker: "Choose a sticker",
149
285
  close: "Close",
150
286
  removeSticker: "Remove sticker",
151
287
  removedSticker: "Removed sticker",
152
288
  selectedSticker: "Selected sticker"
153
- }, t = [
289
+ }, $ = [
154
290
  {
155
291
  id: "1",
156
292
  course_id: "1",
@@ -199,15 +335,23 @@ const i = [
199
335
  grade: "A+",
200
336
  score: 98
201
337
  }
202
- ], s = new Map(
203
- t.map((e) => [e.id, { ...e }])
338
+ ], q = new Map(
339
+ $.map((s) => [s.id, { ...s }])
204
340
  );
205
341
  export {
206
- i as courses,
207
- c as educatorAccessibilityCourseStatistics,
208
- a as mockEducatorAccessibilityStatistic,
209
- r as stickerDescriptions,
210
- o as stickerTranslations,
211
- t as submissions,
212
- s as submissionsStore
342
+ T as MODULES_SMALL_MODULE_COUNT,
343
+ k as MODULE_ITEMS_INLINE_LIMIT,
344
+ c as buildCanvasCourseModules,
345
+ I as courses,
346
+ L as educatorAccessibilityCourseStatistics,
347
+ D as mockEducatorAccessibilityStatistic,
348
+ O as moduleScenariosByCourseId,
349
+ M as modulesCourseEmpty,
350
+ C as modulesCourseFatModule,
351
+ E as modulesCourseLarge,
352
+ v as modulesCourseSmall,
353
+ B as stickerDescriptions,
354
+ x as stickerTranslations,
355
+ $ as submissions,
356
+ q as submissionsStore
213
357
  };
@@ -1,8 +1,10 @@
1
1
  import { RequestHandler } from 'msw';
2
2
  import { courseHandlers } from './courses';
3
3
  import { educatorAccessibilityHandlers } from './educatorAccessibility';
4
+ import { moduleHandlers } from './modules';
4
5
  import { submissionHandlers } from './submissions';
5
6
  import { userColorHandlers } from './userColors';
6
7
  export declare const handlers: RequestHandler[];
7
- export { courseHandlers, educatorAccessibilityHandlers, submissionHandlers, userColorHandlers };
8
+ export { courseHandlers, educatorAccessibilityHandlers, moduleHandlers, submissionHandlers, userColorHandlers, };
9
+ export { buildLinkHeader, resetModuleScenarios } from './modules';
8
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/handlers/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,KAAK,CAAA;AACzC,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAC1C,OAAO,EAAE,6BAA6B,EAAE,MAAM,yBAAyB,CAAA;AACvE,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAEhD,eAAO,MAAM,QAAQ,EAAE,cAAc,EAKpC,CAAA;AAED,OAAO,EAAE,cAAc,EAAE,6BAA6B,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/handlers/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,KAAK,CAAA;AACzC,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAC1C,OAAO,EAAE,6BAA6B,EAAE,MAAM,yBAAyB,CAAA;AACvE,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAC1C,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAEhD,eAAO,MAAM,QAAQ,EAAE,cAAc,EAMpC,CAAA;AAED,OAAO,EACL,cAAc,EACd,6BAA6B,EAC7B,cAAc,EACd,kBAAkB,EAClB,iBAAiB,GAClB,CAAA;AACD,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAA"}
@@ -0,0 +1,11 @@
1
+ import { RequestHandler } from 'msw';
2
+ /** Drops any cached scenario, so a test can start from a clean fixture. */
3
+ export declare function resetModuleScenarios(): void;
4
+ /**
5
+ * An RFC-5988 `Link` header in Canvas's order: current, next, prev, first,
6
+ * last. `next` is absent on the final page — that absence is the only signal a
7
+ * client gets that it has reached the end.
8
+ */
9
+ export declare function buildLinkHeader(url: URL, page: number, perPage: number, totalCount: number): string;
10
+ export declare const moduleHandlers: RequestHandler[];
11
+ //# sourceMappingURL=modules.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"modules.d.ts","sourceRoot":"","sources":["../../src/handlers/modules.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,KAAK,CAAA;AAmDzC,2EAA2E;AAC3E,wBAAgB,oBAAoB,IAAI,IAAI,CAE3C;AAgCD;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE,GAAG,EACR,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,GACjB,MAAM,CAQR;AASD,eAAO,MAAM,cAAc,EAAE,cAAc,EAuC1C,CAAA"}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  export * from './fixtures';
2
- export { courseHandlers, educatorAccessibilityHandlers, handlers, submissionHandlers, userColorHandlers, } from './handlers';
2
+ export { buildLinkHeader, courseHandlers, educatorAccessibilityHandlers, handlers, moduleHandlers, resetModuleScenarios, submissionHandlers, userColorHandlers, } from './handlers';
3
3
  export type * from './types';
4
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAA;AAC1B,OAAO,EACL,cAAc,EACd,6BAA6B,EAC7B,QAAQ,EACR,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,YAAY,CAAA;AACnB,mBAAmB,SAAS,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAA;AAC1B,OAAO,EACL,eAAe,EACf,cAAc,EACd,6BAA6B,EAC7B,QAAQ,EACR,cAAc,EACd,oBAAoB,EACpB,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,YAAY,CAAA;AACnB,mBAAmB,SAAS,CAAA"}
package/dist/index.js CHANGED
@@ -1,70 +1,138 @@
1
- import { courses as c, educatorAccessibilityCourseStatistics as m, submissionsStore as u } from "./fixtures.js";
2
- import { mockEducatorAccessibilityStatistic as y, stickerDescriptions as _, stickerTranslations as S, submissions as h } from "./fixtures.js";
3
- import { http as i, HttpResponse as n } from "msw";
4
- const d = [
1
+ import { courses as I, educatorAccessibilityCourseStatistics as _, moduleScenariosByCourseId as S, submissionsStore as m } from "./fixtures.js";
2
+ import { MODULES_SMALL_MODULE_COUNT as F, MODULE_ITEMS_INLINE_LIMIT as R, buildCanvasCourseModules as O, mockEducatorAccessibilityStatistic as B, modulesCourseEmpty as G, modulesCourseFatModule as $, modulesCourseLarge as K, modulesCourseSmall as X, stickerDescriptions as Y, stickerTranslations as q, submissions as z } from "./fixtures.js";
3
+ import { http as u, HttpResponse as r } from "msw";
4
+ const j = [
5
5
  // GET /api/v1/courses
6
- i.get("/api/v1/courses", () => n.json(c)),
6
+ u.get("/api/v1/courses", () => r.json(I)),
7
7
  // GET /api/v1/courses/:courseId (courseId may be a numeric id or a `uuid:<uuid>` ref)
8
- i.get("/api/v1/courses/:courseId", ({ params: e }) => {
9
- const s = String(e.courseId).replace(/^uuid:/, ""), o = c.find((t) => t.id === s || t.uuid === s);
10
- return o ? n.json(o) : n.json({ errors: [{ message: "Course not found" }] }, { status: 404 });
8
+ u.get("/api/v1/courses/:courseId", ({ params: e }) => {
9
+ const s = String(e.courseId).replace(/^uuid:/, ""), n = I.find((t) => t.id === s || t.uuid === s);
10
+ return n ? r.json(n) : r.json({ errors: [{ message: "Course not found" }] }, { status: 404 });
11
11
  })
12
- ], b = [
13
- i.get("/api/v1/users/:userId/educator_accessibility_course_statistics", () => n.json(m))
14
- ], g = 300, l = [
12
+ ], P = [
13
+ u.get("/api/v1/users/:userId/educator_accessibility_course_statistics", () => r.json(_))
14
+ ], M = 10, k = 100, l = () => r.json(
15
+ { errors: [{ message: "The specified resource does not exist." }] },
16
+ {
17
+ status: 404
18
+ }
19
+ ), g = /* @__PURE__ */ new Map(), b = (e) => {
20
+ const s = g.get(e);
21
+ if (s) return s;
22
+ const n = S[e];
23
+ if (!n) return;
24
+ const t = n();
25
+ return g.set(e, t), t;
26
+ };
27
+ function H() {
28
+ g.clear();
29
+ }
30
+ const f = (e) => {
31
+ const s = Number.parseInt(e.searchParams.get("per_page") ?? "", 10), n = Number.parseInt(e.searchParams.get("page") ?? "", 10);
32
+ return {
33
+ perPage: Number.isFinite(s) && s > 0 ? Math.min(s, k) : M,
34
+ page: Number.isFinite(n) && n > 0 ? n : 1
35
+ };
36
+ }, v = (e) => [...e.searchParams.getAll("include[]"), ...e.searchParams.getAll("include")].flatMap(
37
+ (s) => s.split(",")
38
+ ), y = (e, s, n) => {
39
+ const t = new URL(e.href);
40
+ return t.searchParams.set("page", String(s)), t.searchParams.set("per_page", String(n)), t.href;
41
+ };
42
+ function h(e, s, n, t) {
43
+ const o = Math.max(1, Math.ceil(t / n)), i = [["current", s]];
44
+ return s < o && i.push(["next", s + 1]), s > 1 && i.push(["prev", s - 1]), i.push(["first", 1], ["last", o]), i.map(([a, c]) => `<${y(e, c, n)}>; rel="${a}"`).join(",");
45
+ }
46
+ const L = (e) => {
47
+ const s = { ...e };
48
+ return delete s.items, s;
49
+ }, A = [
50
+ // GET /api/v1/courses/:courseId/modules
51
+ u.get("/api/v1/courses/:courseId/modules", ({ params: e, request: s }) => {
52
+ const n = b(String(e.courseId));
53
+ if (!n) return l();
54
+ const t = new URL(s.url), { page: o, perPage: i } = f(t), a = v(t).includes("items"), c = (o - 1) * i, d = n.modules.slice(c, c + i).map((p) => a ? p : L(p));
55
+ return r.json(d, {
56
+ headers: { Link: h(t, o, i, n.modules.length) }
57
+ });
58
+ }),
59
+ // GET /api/v1/courses/:courseId/modules/:moduleId/items
60
+ u.get("/api/v1/courses/:courseId/modules/:moduleId/items", ({ params: e, request: s }) => {
61
+ const n = b(String(e.courseId));
62
+ if (!n) return l();
63
+ const t = String(e.moduleId), o = n.itemsByModuleId[t];
64
+ if (!o) return l();
65
+ const i = new URL(s.url), { page: a, perPage: c } = f(i), d = (a - 1) * c;
66
+ return r.json(o.slice(d, d + c), {
67
+ headers: { Link: h(i, a, c, o.length) }
68
+ });
69
+ })
70
+ ], C = 300, E = [
15
71
  // GET submission
16
- i.get(
72
+ u.get(
17
73
  "/api/v1/courses/:courseId/assignments/:assignmentId/submissions/:submissionId",
18
74
  ({ params: e }) => {
19
- const s = u.get(e.submissionId);
20
- return s ? n.json(s) : n.json({ errors: [{ message: "Submission not found" }] }, { status: 404 });
75
+ const s = m.get(e.submissionId);
76
+ return s ? r.json(s) : r.json({ errors: [{ message: "Submission not found" }] }, { status: 404 });
21
77
  }
22
78
  ),
23
79
  // PATCH submission (update sticker)
24
- i.patch(
80
+ u.patch(
25
81
  "/api/v1/courses/:courseId/assignments/:assignmentId/submissions/:submissionId",
26
82
  async ({ params: e, request: s }) => {
27
- const { submissionId: o } = e, t = await s.json(), r = u.get(o);
28
- return r ? (t.submission && "sticker" in t.submission && t.submission.sticker !== void 0 && (r.sticker = t.submission.sticker, u.set(o, r)), await new Promise((a) => setTimeout(a, g)), n.json(r)) : n.json({ errors: [{ message: "Submission not found" }] }, { status: 404 });
83
+ const { submissionId: n } = e, t = await s.json(), o = m.get(n);
84
+ return o ? (t.submission && "sticker" in t.submission && t.submission.sticker !== void 0 && (o.sticker = t.submission.sticker, m.set(n, o)), await new Promise((i) => setTimeout(i, C)), r.json(o)) : r.json({ errors: [{ message: "Submission not found" }] }, { status: 404 });
29
85
  }
30
86
  ),
31
87
  // GET all submissions for an assignment
32
- i.get("/api/v1/courses/:courseId/assignments/:assignmentId/submissions", ({ params: e }) => {
33
- const s = Array.from(u.values()).filter(
34
- (o) => o.course_id === e.courseId && o.assignment_id === e.assignmentId
88
+ u.get("/api/v1/courses/:courseId/assignments/:assignmentId/submissions", ({ params: e }) => {
89
+ const s = Array.from(m.values()).filter(
90
+ (n) => n.course_id === e.courseId && n.assignment_id === e.assignmentId
35
91
  );
36
- return n.json(s);
92
+ return r.json(s);
37
93
  })
38
- ], p = [
94
+ ], w = [
39
95
  // PUT /api/v1/users/:userId/colors/:assetString -> persist a course color
40
- i.put("/api/v1/users/:userId/colors/:assetString", async ({ request: e }) => {
96
+ u.put("/api/v1/users/:userId/colors/:assetString", async ({ request: e }) => {
41
97
  const s = await e.json().catch(() => ({}));
42
- return n.json({ hexcode: s.hexcode ?? "" });
98
+ return r.json({ hexcode: s.hexcode ?? "" });
43
99
  }),
44
100
  // PUT /api/v1/users/self/course_nicknames/:courseId -> set a nickname
45
- i.put("/api/v1/users/self/course_nicknames/:courseId", async ({ request: e }) => {
101
+ u.put("/api/v1/users/self/course_nicknames/:courseId", async ({ request: e }) => {
46
102
  const s = await e.json().catch(() => ({}));
47
- return n.json({ nickname: s.nickname ?? "", name: s.nickname ?? "" });
103
+ return r.json({ nickname: s.nickname ?? "", name: s.nickname ?? "" });
48
104
  }),
49
105
  // DELETE /api/v1/users/self/course_nicknames/:courseId -> clear a nickname
50
- i.delete("/api/v1/users/self/course_nicknames/:courseId", () => n.json({ nickname: null, name: "" }))
51
- ], j = [
52
- ...d,
53
- ...b,
54
- ...l,
55
- ...p
106
+ u.delete("/api/v1/users/self/course_nicknames/:courseId", () => r.json({ nickname: null, name: "" }))
107
+ ], N = [
108
+ ...j,
109
+ ...P,
110
+ ...A,
111
+ ...E,
112
+ ...w
56
113
  ];
57
114
  export {
58
- d as courseHandlers,
59
- c as courses,
60
- m as educatorAccessibilityCourseStatistics,
61
- b as educatorAccessibilityHandlers,
62
- j as handlers,
63
- y as mockEducatorAccessibilityStatistic,
64
- _ as stickerDescriptions,
65
- S as stickerTranslations,
66
- l as submissionHandlers,
67
- h as submissions,
68
- u as submissionsStore,
69
- p as userColorHandlers
115
+ F as MODULES_SMALL_MODULE_COUNT,
116
+ R as MODULE_ITEMS_INLINE_LIMIT,
117
+ O as buildCanvasCourseModules,
118
+ h as buildLinkHeader,
119
+ j as courseHandlers,
120
+ I as courses,
121
+ _ as educatorAccessibilityCourseStatistics,
122
+ P as educatorAccessibilityHandlers,
123
+ N as handlers,
124
+ B as mockEducatorAccessibilityStatistic,
125
+ A as moduleHandlers,
126
+ S as moduleScenariosByCourseId,
127
+ G as modulesCourseEmpty,
128
+ $ as modulesCourseFatModule,
129
+ K as modulesCourseLarge,
130
+ X as modulesCourseSmall,
131
+ H as resetModuleScenarios,
132
+ Y as stickerDescriptions,
133
+ q as stickerTranslations,
134
+ E as submissionHandlers,
135
+ z as submissions,
136
+ m as submissionsStore,
137
+ w as userColorHandlers
70
138
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@instructure/platform-mock-canvas-api",
3
- "version": "0.4.0",
3
+ "version": "0.5.1",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -12,7 +12,7 @@
12
12
  },
13
13
  "./fixtures": {
14
14
  "types": "./dist/fixtures/index.d.ts",
15
- "import": "./dist/fixtures/index.js"
15
+ "import": "./dist/fixtures.js"
16
16
  }
17
17
  },
18
18
  "files": [