@forwardimpact/libharness 0.1.5 → 0.1.7

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.
package/fixture/index.js CHANGED
@@ -4,3 +4,4 @@ export {
4
4
  createDeferred,
5
5
  } from "./assertions.js";
6
6
  export * from "./services.js";
7
+ export * from "./pathway.js";
@@ -0,0 +1,293 @@
1
+ /**
2
+ * Pathway test fixtures for creating realistic test data
3
+ * Provides mock disciplines, levels, tracks, skills, and behaviours
4
+ */
5
+
6
+ /**
7
+ * Creates a mock level definition
8
+ * @param {object} overrides - Properties to override
9
+ * @returns {object} Mock level
10
+ */
11
+ export function createTestLevel(overrides = {}) {
12
+ return {
13
+ id: "mid",
14
+ name: "Mid-Level",
15
+ ordinalRank: 3,
16
+ professionalTitle: "Level III",
17
+ managementTitle: "Manager",
18
+ baseSkillProficiencies: {
19
+ primary: "working",
20
+ secondary: "foundational",
21
+ broad: "awareness",
22
+ },
23
+ baseBehaviourMaturity: "developing",
24
+ expectations: {},
25
+ ...overrides,
26
+ };
27
+ }
28
+
29
+ /**
30
+ * Creates a set of mock levels spanning junior to senior
31
+ * @returns {object[]} Array of mock levels
32
+ */
33
+ export function createTestLevels() {
34
+ return [
35
+ createTestLevel({
36
+ id: "junior",
37
+ name: "Junior",
38
+ ordinalRank: 1,
39
+ professionalTitle: "Level I",
40
+ managementTitle: "Team Lead",
41
+ baseSkillProficiencies: {
42
+ primary: "foundational",
43
+ secondary: "awareness",
44
+ broad: "awareness",
45
+ },
46
+ baseBehaviourMaturity: "emerging",
47
+ }),
48
+ createTestLevel({
49
+ id: "mid",
50
+ name: "Mid-Level",
51
+ ordinalRank: 3,
52
+ professionalTitle: "Level III",
53
+ managementTitle: "Manager",
54
+ baseSkillProficiencies: {
55
+ primary: "working",
56
+ secondary: "foundational",
57
+ broad: "awareness",
58
+ },
59
+ baseBehaviourMaturity: "developing",
60
+ }),
61
+ createTestLevel({
62
+ id: "senior",
63
+ name: "Senior",
64
+ ordinalRank: 5,
65
+ professionalTitle: "Senior",
66
+ managementTitle: "Senior Manager",
67
+ baseSkillProficiencies: {
68
+ primary: "practitioner",
69
+ secondary: "working",
70
+ broad: "foundational",
71
+ },
72
+ baseBehaviourMaturity: "practicing",
73
+ }),
74
+ createTestLevel({
75
+ id: "staff",
76
+ name: "Staff",
77
+ ordinalRank: 7,
78
+ professionalTitle: "Staff",
79
+ managementTitle: "Director",
80
+ baseSkillProficiencies: {
81
+ primary: "expert",
82
+ secondary: "practitioner",
83
+ broad: "working",
84
+ },
85
+ baseBehaviourMaturity: "role_modeling",
86
+ }),
87
+ ];
88
+ }
89
+
90
+ /**
91
+ * Creates a mock skill definition
92
+ * @param {object} overrides - Properties to override
93
+ * @returns {object} Mock skill
94
+ */
95
+ export function createTestSkill(overrides = {}) {
96
+ return {
97
+ id: "test_skill",
98
+ name: "Test Skill",
99
+ capability: "delivery",
100
+ isHumanOnly: false,
101
+ proficiencyDescriptions: {
102
+ awareness: "Basic awareness",
103
+ foundational: "Foundational understanding",
104
+ working: "Working proficiency",
105
+ practitioner: "Practitioner level",
106
+ expert: "Expert level",
107
+ },
108
+ ...overrides,
109
+ };
110
+ }
111
+
112
+ /**
113
+ * Creates a set of mock skills across capabilities
114
+ * @returns {object[]} Array of mock skills
115
+ */
116
+ export function createTestSkills() {
117
+ return [
118
+ createTestSkill({
119
+ id: "coding",
120
+ name: "Coding",
121
+ capability: "delivery",
122
+ }),
123
+ createTestSkill({
124
+ id: "testing",
125
+ name: "Testing",
126
+ capability: "delivery",
127
+ }),
128
+ createTestSkill({
129
+ id: "architecture",
130
+ name: "Architecture",
131
+ capability: "scale",
132
+ }),
133
+ createTestSkill({
134
+ id: "observability",
135
+ name: "Observability",
136
+ capability: "scale",
137
+ }),
138
+ createTestSkill({
139
+ id: "ai_tools",
140
+ name: "AI Tools",
141
+ capability: "ai",
142
+ }),
143
+ createTestSkill({
144
+ id: "mentoring",
145
+ name: "Mentoring",
146
+ capability: "delivery",
147
+ isHumanOnly: true,
148
+ }),
149
+ ];
150
+ }
151
+
152
+ /**
153
+ * Creates a mock discipline definition
154
+ * @param {object} overrides - Properties to override
155
+ * @returns {object} Mock discipline
156
+ */
157
+ export function createTestDiscipline(overrides = {}) {
158
+ return {
159
+ id: "software_engineering",
160
+ name: "Software Engineering",
161
+ roleTitle: "Software Engineer",
162
+ isProfessional: true,
163
+ isManagement: false,
164
+ coreSkills: ["coding", "testing"],
165
+ supportingSkills: ["architecture"],
166
+ broadSkills: ["observability"],
167
+ behaviourModifiers: {},
168
+ validTracks: [],
169
+ ...overrides,
170
+ };
171
+ }
172
+
173
+ /**
174
+ * Creates a mock track definition
175
+ * @param {object} overrides - Properties to override
176
+ * @returns {object} Mock track
177
+ */
178
+ export function createTestTrack(overrides = {}) {
179
+ return {
180
+ id: "platform",
181
+ name: "Platform",
182
+ skillModifiers: { scale: 1, delivery: -1 },
183
+ behaviourModifiers: {},
184
+ ...overrides,
185
+ };
186
+ }
187
+
188
+ /**
189
+ * Creates a mock behaviour definition
190
+ * @param {object} overrides - Properties to override
191
+ * @returns {object} Mock behaviour
192
+ */
193
+ export function createTestBehaviour(overrides = {}) {
194
+ return {
195
+ id: "collaboration",
196
+ name: "Collaboration",
197
+ maturityDescriptions: {
198
+ emerging: "Beginning to collaborate",
199
+ developing: "Developing collaboration",
200
+ practicing: "Practicing collaboration",
201
+ role_modeling: "Role modeling collaboration",
202
+ exemplifying: "Exemplifying collaboration",
203
+ },
204
+ ...overrides,
205
+ };
206
+ }
207
+
208
+ /**
209
+ * Creates a set of mock behaviours
210
+ * @returns {object[]} Array of mock behaviours
211
+ */
212
+ export function createTestBehaviours() {
213
+ return [
214
+ createTestBehaviour({ id: "collaboration", name: "Collaboration" }),
215
+ createTestBehaviour({ id: "communication", name: "Communication" }),
216
+ createTestBehaviour({ id: "leadership", name: "Leadership" }),
217
+ ];
218
+ }
219
+
220
+ /**
221
+ * Creates a mock capability definition
222
+ * @param {object} overrides - Properties to override
223
+ * @returns {object} Mock capability
224
+ */
225
+ export function createTestCapability(overrides = {}) {
226
+ return {
227
+ id: "delivery",
228
+ name: "Delivery",
229
+ emojiIcon: "🚀",
230
+ ordinalRank: 1,
231
+ professionalResponsibilities: {
232
+ foundational: "Delivers assigned tasks",
233
+ working: "Delivers features independently",
234
+ practitioner: "Leads delivery across team",
235
+ expert: "Defines delivery strategy",
236
+ },
237
+ managementResponsibilities: {
238
+ foundational: "Manages delivery timelines",
239
+ working: "Owns team delivery",
240
+ practitioner: "Leads multi-team delivery",
241
+ expert: "Shapes delivery culture",
242
+ },
243
+ ...overrides,
244
+ };
245
+ }
246
+
247
+ /**
248
+ * Creates a mock driver definition
249
+ * @param {object} overrides - Properties to override
250
+ * @returns {object} Mock driver
251
+ */
252
+ export function createTestDriver(overrides = {}) {
253
+ return {
254
+ id: "velocity",
255
+ name: "Velocity",
256
+ contributingSkills: ["coding", "testing"],
257
+ contributingBehaviours: ["collaboration"],
258
+ ...overrides,
259
+ };
260
+ }
261
+
262
+ /**
263
+ * Creates a mock skill matrix entry
264
+ * @param {object} overrides - Properties to override
265
+ * @returns {object} Mock skill matrix entry
266
+ */
267
+ export function createTestSkillEntry(overrides = {}) {
268
+ return {
269
+ skillId: "coding",
270
+ skillName: "Coding",
271
+ capability: "delivery",
272
+ isHumanOnly: false,
273
+ type: "primary",
274
+ proficiency: "working",
275
+ proficiencyDescription: "Working proficiency",
276
+ ...overrides,
277
+ };
278
+ }
279
+
280
+ /**
281
+ * Creates a mock behaviour profile entry
282
+ * @param {object} overrides - Properties to override
283
+ * @returns {object} Mock behaviour profile entry
284
+ */
285
+ export function createTestBehaviourEntry(overrides = {}) {
286
+ return {
287
+ behaviourId: "collaboration",
288
+ behaviourName: "Collaboration",
289
+ maturity: "developing",
290
+ maturityDescription: "Developing collaboration",
291
+ ...overrides,
292
+ };
293
+ }
package/mock/fs.js ADDED
@@ -0,0 +1,112 @@
1
+ import { mock } from "node:test";
2
+
3
+ /**
4
+ * Creates a mock filesystem backed by an in-memory Map
5
+ * @param {Object<string, string>} files - Initial file contents keyed by path
6
+ * @returns {object} Mock fs with readFile, writeFile, readdir, stat, mkdir, access, copyFile
7
+ */
8
+ export function createMockFs(files = {}) {
9
+ const data = new Map(Object.entries(files));
10
+ const dirs = new Set();
11
+
12
+ // Auto-create parent directories for initial files
13
+ for (const path of data.keys()) {
14
+ const parts = path.split("/");
15
+ for (let i = 1; i < parts.length; i++) {
16
+ dirs.add(parts.slice(0, i).join("/"));
17
+ }
18
+ }
19
+
20
+ return {
21
+ data,
22
+ dirs,
23
+ readFile: mock.fn(async (path, encoding) => {
24
+ const content = data.get(path);
25
+ if (content === undefined) {
26
+ const err = new Error(
27
+ `ENOENT: no such file or directory, open '${path}'`,
28
+ );
29
+ err.code = "ENOENT";
30
+ throw err;
31
+ }
32
+ return encoding ? content : Buffer.from(content);
33
+ }),
34
+ writeFile: mock.fn(async (path, content) => {
35
+ data.set(
36
+ path,
37
+ typeof content === "string" ? content : content.toString(),
38
+ );
39
+ }),
40
+ readdir: mock.fn(async (path) => {
41
+ const entries = [];
42
+ const prefix = path.endsWith("/") ? path : `${path}/`;
43
+ for (const key of data.keys()) {
44
+ if (key.startsWith(prefix)) {
45
+ const rest = key.slice(prefix.length);
46
+ const name = rest.split("/")[0];
47
+ if (name && !entries.includes(name)) {
48
+ entries.push(name);
49
+ }
50
+ }
51
+ }
52
+ return entries;
53
+ }),
54
+ stat: mock.fn(async (path) => {
55
+ if (data.has(path)) {
56
+ return { isFile: () => true, isDirectory: () => false };
57
+ }
58
+ if (dirs.has(path)) {
59
+ return { isFile: () => false, isDirectory: () => true };
60
+ }
61
+ const err = new Error(
62
+ `ENOENT: no such file or directory, stat '${path}'`,
63
+ );
64
+ err.code = "ENOENT";
65
+ throw err;
66
+ }),
67
+ mkdir: mock.fn(async (path) => {
68
+ dirs.add(path);
69
+ }),
70
+ access: mock.fn(async (path) => {
71
+ if (!data.has(path) && !dirs.has(path)) {
72
+ const err = new Error(
73
+ `ENOENT: no such file or directory, access '${path}'`,
74
+ );
75
+ err.code = "ENOENT";
76
+ throw err;
77
+ }
78
+ }),
79
+ copyFile: mock.fn(async (src, dest) => {
80
+ const content = data.get(src);
81
+ if (content === undefined) {
82
+ const err = new Error(
83
+ `ENOENT: no such file or directory, copyFile '${src}'`,
84
+ );
85
+ err.code = "ENOENT";
86
+ throw err;
87
+ }
88
+ data.set(dest, content);
89
+ }),
90
+ existsSync: mock.fn((path) => data.has(path) || dirs.has(path)),
91
+ readFileSync: mock.fn((path, encoding) => {
92
+ const content = data.get(path);
93
+ if (content === undefined) {
94
+ const err = new Error(
95
+ `ENOENT: no such file or directory, open '${path}'`,
96
+ );
97
+ err.code = "ENOENT";
98
+ throw err;
99
+ }
100
+ return encoding ? content : Buffer.from(content);
101
+ }),
102
+ writeFileSync: mock.fn((path, content) => {
103
+ data.set(
104
+ path,
105
+ typeof content === "string" ? content : content.toString(),
106
+ );
107
+ }),
108
+ mkdirSync: mock.fn((path) => {
109
+ dirs.add(path);
110
+ }),
111
+ };
112
+ }
package/mock/index.js CHANGED
@@ -24,3 +24,4 @@ export {
24
24
  createMockToolClient,
25
25
  } from "./clients.js";
26
26
  export { createMockServiceCallbacks } from "./service-callbacks.js";
27
+ export { createMockFs } from "./fs.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forwardimpact/libharness",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "Test harness and mock infrastructure for guide tests",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -28,5 +28,8 @@
28
28
  "license": "MIT",
29
29
  "peerDependencies": {
30
30
  "@forwardimpact/libtype": "*"
31
+ },
32
+ "publishConfig": {
33
+ "access": "public"
31
34
  }
32
35
  }