cassproject 5.0.22 → 5.0.23

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.
Files changed (105) hide show
  1. package/package.json +47 -50
  2. package/src/com/eduworks/ec/array/EcArray.js +1 -1
  3. package/src/com/eduworks/ec/array/EcAsyncHelper.js +1 -1
  4. package/src/com/eduworks/ec/array/EcObject.js +1 -1
  5. package/src/com/eduworks/ec/crypto/EcAes.js +1 -1
  6. package/src/com/eduworks/ec/crypto/EcAesCtrAsync.js +20 -22
  7. package/src/com/eduworks/ec/crypto/EcCrypto.js +8 -4
  8. package/src/com/eduworks/ec/crypto/EcPk.js +7 -7
  9. package/src/com/eduworks/ec/crypto/EcPpk.js +7 -7
  10. package/src/com/eduworks/ec/crypto/EcRsaOaepAsync.js +20 -19
  11. package/src/com/eduworks/ec/crypto/EcRsaOaepAsyncWorker.js +7 -6
  12. package/src/com/eduworks/ec/crypto/forgeAsyncNode.js +8 -8
  13. package/src/com/eduworks/ec/graph/EcDirectedGraph.js +1 -2
  14. package/src/com/eduworks/ec/graph/EcFrameworkGraph.js +18 -24
  15. package/src/com/eduworks/ec/promises/helpers.js +1 -3
  16. package/src/com/eduworks/ec/remote/EcRemote.js +9 -9
  17. package/src/com/eduworks/ec/task/EcAsyncTask.js +5 -20
  18. package/src/com/eduworks/ec/task/EcAsyncTaskManager.js +3 -4
  19. package/src/com/eduworks/ec/task/EcAsyncTaskWorker.js +9 -11
  20. package/src/com/eduworks/schema/cfd/competency/CfdFramework.js +36 -37
  21. package/src/com/eduworks/schema/ebac/EbacContact.js +3 -9
  22. package/src/com/eduworks/schema/ebac/EbacContactGrant.js +3 -9
  23. package/src/com/eduworks/schema/ebac/EbacCredential.js +3 -9
  24. package/src/com/eduworks/schema/ebac/EbacCredentialCommit.js +3 -9
  25. package/src/com/eduworks/schema/ebac/EbacCredentialRequest.js +3 -9
  26. package/src/com/eduworks/schema/ebac/EbacCredentials.js +3 -9
  27. package/src/com/eduworks/schema/ebac/EbacEncryptedSecret.js +3 -9
  28. package/src/com/eduworks/schema/ebac/EbacEncryptedValue.js +5 -12
  29. package/src/com/eduworks/schema/ebac/EbacSignature.js +1 -6
  30. package/src/index/env.js +6 -3
  31. package/src/index/polyfills.js +13 -27
  32. package/src/org/angles/schema/angles/VideoStory.js +1 -1
  33. package/src/org/cass/competency/EcCompetency.js +1 -2
  34. package/src/org/cass/competency/EcDirectory.js +1 -2
  35. package/src/org/cass/competency/EcFramework.js +9 -12
  36. package/src/org/cass/exporter/CSVExport.js +12 -13
  37. package/src/org/cass/importer/ASNImport.js +3 -6
  38. package/src/org/cass/importer/CSVImport.js +9 -17
  39. package/src/org/cass/importer/CTDLASNCSVConceptImport.js +25 -121
  40. package/src/org/cass/importer/CTDLASNCSVImport.js +88 -133
  41. package/src/org/cass/importer/FrameworkImport.js +16 -30
  42. package/src/org/cass/importer/MedbiqImport.js +3 -4
  43. package/src/org/cass/importer/TabStructuredImport.js +6 -6
  44. package/src/org/cass/profile/EcAssertion.js +40 -51
  45. package/src/org/cassproject/ebac/identity/EcContactGrant.js +2 -2
  46. package/src/org/cassproject/ebac/identity/EcIdentityManager.js +56 -62
  47. package/src/org/cassproject/ebac/identity/remote/EcRemoteIdentityManager.js +51 -62
  48. package/src/org/cassproject/ebac/identity/remote/OAuth2FileBasedRemoteIdentityManager.js +56 -70
  49. package/src/org/cassproject/ebac/identity/remote/RemoteIdentityManagerInterface.js +3 -3
  50. package/src/org/cassproject/ebac/repository/EcEncryptedValue.js +51 -44
  51. package/src/org/cassproject/ebac/repository/EcRepository.js +228 -301
  52. package/src/org/cassproject/general/repository/GeneralFile.js +8 -9
  53. package/src/org/cassproject/schema/cass/competency/Competency.js +10 -11
  54. package/src/org/cassproject/schema/cass/competency/Framework.js +10 -11
  55. package/src/org/cassproject/schema/cass/competency/Level.js +10 -11
  56. package/src/org/cassproject/schema/cass/competency/Relation.js +10 -11
  57. package/src/org/cassproject/schema/cass/competency/RollupRule.js +7 -7
  58. package/src/org/cassproject/schema/cass/profile/Assertion.js +20 -27
  59. package/src/org/cassproject/schema/cass/profile/AssertionEnvelope.js +3 -5
  60. package/src/org/cassproject/schema/general/EcRemoteLinkedData.js +41 -44
  61. package/src/org/json/ld/EcLinkedData.js +10 -12
  62. package/src/org/schema/EcOrganization.js +10 -10
  63. package/src/org/schema/EcQuestion.js +8 -8
  64. package/src/org/w3/skos/Concept.js +6 -6
  65. package/src/org/w3/skos/ConceptScheme.js +6 -6
  66. package/src/org/w3/skos/EcConcept.js +1 -2
  67. package/src/org/w3/skos/EcConceptScheme.js +1 -2
  68. package/src/test/0.start.test.js +1 -1
  69. package/src/test/0.testLibraries.test.js +172 -0
  70. package/src/test/1.ASNImport.test.js +122 -0
  71. package/src/test/1.CSVExport.test.js +57 -0
  72. package/src/test/1.CSVImport.test.js +185 -0
  73. package/src/test/1.CTDLASNCSVConceptImport.test.js +348 -0
  74. package/src/test/1.CTDLASNCSVImport.import.test.js +497 -0
  75. package/src/test/1.Ebac.schema.test.js +107 -0
  76. package/src/test/1.EcAssertion.decrypt.test.js +98 -0
  77. package/src/test/1.EcAssertion.pinned.test.js +160 -0
  78. package/src/test/1.EcCrypto.formats.test.js +293 -0
  79. package/src/test/1.EcCrypto.generateUUID.test.js +47 -0
  80. package/src/test/1.EcEncryptedValue.helpers.test.js +236 -0
  81. package/src/test/1.EcHelpers.misc.test.js +259 -0
  82. package/src/test/1.EcIdentityManager.keys.test.js +439 -0
  83. package/src/test/1.EcOrganization.rekey.test.js +68 -0
  84. package/src/test/1.EcRemote.responses.test.js +155 -0
  85. package/src/test/1.EcRemoteIdentityManager.flows.test.js +326 -0
  86. package/src/test/1.EcRemoteLinkedData.pinned.test.js +473 -0
  87. package/src/test/1.EcRepository.l0.test.js +14 -5
  88. package/src/test/1.EcRepository.l1.test.js +16 -7
  89. package/src/test/1.EcRepository.l2.test.js +20 -11
  90. package/src/test/1.EcRepository.unit.test.js +961 -0
  91. package/src/test/1.EcRsaOaep.test.js +2 -12
  92. package/src/test/1.EcRsaOaepAsync.test.js +2 -12
  93. package/src/test/1.EcRsaOaepAsyncWorker.test.js +2 -12
  94. package/src/test/1.EcRsaOaepAsyncWorker.workers.test.js +147 -0
  95. package/src/test/1.FrameworkImport.test.js +170 -0
  96. package/src/test/1.MedbiqImport.test.js +92 -0
  97. package/src/test/1.OAuth2FileBasedRemoteIdentityManager.test.js +399 -0
  98. package/src/test/1.TabStructuredImport.test.js +61 -0
  99. package/src/test/1.polyfills.test.js +89 -0
  100. package/src/test/1.schema.competency.pinned.test.js +384 -0
  101. package/src/test/3.EcFrameworkGraph.test.js +3 -2
  102. package/src/test/4.schema.test.js +33 -28
  103. package/src/test/4.skyId.test.js +4 -2
  104. package/src/test/cypressValue.js +9 -0
  105. package/src/test/2.EcCompetency.test.js +0 -17
@@ -71,8 +71,8 @@ module.exports = class EcOrganization extends schema.Organization {
71
71
  throw new Error("Employee is not Array");
72
72
  let ary = this.employee;
73
73
  let psid = person.shortId();
74
- for (let i = 0; i < ary.length; i++) {
75
- if (ary[i] == psid) return;
74
+ for (let employee of ary) {
75
+ if (employee == psid) return;
76
76
  }
77
77
  ary.push(psid);
78
78
  }
@@ -110,7 +110,7 @@ module.exports = class EcOrganization extends schema.Organization {
110
110
  let empAry = this.employee;
111
111
  for (let i = 0; i < membAry.length; i++) {
112
112
  let id = membAry[i];
113
- if (id.toLowerCase().indexOf("person") > -1) {
113
+ if (id.toLowerCase().includes("person")) {
114
114
  if (empAry.indexOf(id) <= -1) {
115
115
  empAry.push(id);
116
116
  }
@@ -132,8 +132,8 @@ module.exports = class EcOrganization extends schema.Organization {
132
132
  if (ppkList == null) return JSON.stringify([]);
133
133
  else {
134
134
  let pemArray = [];
135
- for (let i = 0; i < ppkList.length; i++) {
136
- pemArray.push(ppkList[i].toPem());
135
+ for (let ppk of ppkList) {
136
+ pemArray.push(ppk.toPem());
137
137
  }
138
138
  return JSON.stringify(pemArray);
139
139
  }
@@ -167,7 +167,6 @@ module.exports = class EcOrganization extends schema.Organization {
167
167
  let msg = "Repository cannot be null for a rekey operation";
168
168
  if (failure != null) failure(msg);
169
169
  else global.auditLogger.report(global.auditLogger.LogCategory.SYSTEM, global.auditLogger.Severity.ERROR, "EcOrgRekeyAndSave", msg);
170
- return;
171
170
  } else {
172
171
  let oldKey = await this.getCurrentOrgKey();
173
172
  let newKey = EcPpk.generateKey();
@@ -180,7 +179,7 @@ module.exports = class EcOrganization extends schema.Organization {
180
179
  oldKey,
181
180
  newKey
182
181
  );
183
- this.addOrgKey(newKey);
182
+ await this.addOrgKey(newKey);
184
183
  let newKeys = await EcEncryptedValue.encryptValue(
185
184
  this.ppkListToPemArrayString(await this.getOrgKeys()),
186
185
  EcOrganization.ORG_PPK_SET_KEY,
@@ -247,8 +246,8 @@ module.exports = class EcOrganization extends schema.Organization {
247
246
  let ev = new EcEncryptedValue();
248
247
  ev.copyFrom(o);
249
248
  let orgKeysPPKPems = JSON.parse(await ev.decryptIntoString(null, null, eim));
250
- for (let i = 0; i < orgKeysPPKPems.length; i++) {
251
- orgKeys.push(EcPpk.fromPem(orgKeysPPKPems[i]));
249
+ for (let pem of orgKeysPPKPems) {
250
+ orgKeys.push(EcPpk.fromPem(pem));
252
251
  }
253
252
  } else if (o) {
254
253
  orgKeys = o;
@@ -281,7 +280,8 @@ module.exports = class EcOrganization extends schema.Organization {
281
280
  upgrade(eim) {
282
281
  super.upgrade();
283
282
  this.movePersonMembersToEmployee();
284
- this.moveKeyField(eim);
283
+ //upgrade() is synchronous; the key move finishes in the background as before.
284
+ void this.moveKeyField(eim);
285
285
  }
286
286
  /**
287
287
  * Attempts to find and return the organization's fingerprint from the id.
@@ -52,14 +52,14 @@ module.exports = class EcQuestion extends schema.Question {
52
52
  return EcQuestion.HAND_GRADED_ESSAY;
53
53
  }
54
54
  if (this.suggestedAnswer == null) {
55
- if (this.text != null && this.text.indexOf("__") != -1) {
55
+ if (this.text?.includes("__")) {
56
56
  return EcQuestion.FILL_IN_THE_BLANK;
57
57
  }
58
58
  return EcQuestion.SHORT_ANSWER;
59
59
  }
60
60
  let l = this.suggestedAnswer.length;
61
61
  if (l == 0) {
62
- if (this.text != null && this.text.indexOf("__") != -1) {
62
+ if (this.text?.includes("__")) {
63
63
  return EcQuestion.FILL_IN_THE_BLANK;
64
64
  }
65
65
  return EcQuestion.SHORT_ANSWER;
@@ -75,7 +75,7 @@ module.exports = class EcQuestion extends schema.Question {
75
75
  cementAnswerId(id) {
76
76
  if (this.acceptedAnswer != null) {
77
77
  if (!EcArray.isArray(this.acceptedAnswer)) {
78
- throw new Error("Accepted Answer is not Array");
78
+ throw new Error("Accepted Answer is not Array"); // NOSONAR - A TypeError would change the name and toString() callers see.
79
79
  }
80
80
  let ary = this.acceptedAnswer;
81
81
  for (let i = 0; i < ary.length; i++) {
@@ -89,7 +89,7 @@ module.exports = class EcQuestion extends schema.Question {
89
89
  }
90
90
  if (this.suggestedAnswer != null) {
91
91
  if (!EcArray.isArray(this.suggestedAnswer)) {
92
- throw new Error("Suggested Answer is not Array");
92
+ throw new Error("Suggested Answer is not Array"); // NOSONAR - A TypeError would change the name and toString() callers see.
93
93
  }
94
94
  let ary = this.suggestedAnswer;
95
95
  for (let i = 0; i < ary.length; i++) {
@@ -119,7 +119,7 @@ module.exports = class EcQuestion extends schema.Question {
119
119
  this["acceptedAnswer"] = [];
120
120
  }
121
121
  if (!EcArray.isArray(this.acceptedAnswer)) {
122
- throw new Error("Accepted Answer is not Array");
122
+ throw new Error("Accepted Answer is not Array"); // NOSONAR - A TypeError would change the name and toString() callers see.
123
123
  }
124
124
  let ary = this.acceptedAnswer;
125
125
  ary.push(answer.id);
@@ -129,7 +129,7 @@ module.exports = class EcQuestion extends schema.Question {
129
129
  this["suggestedAnswer"] = [];
130
130
  }
131
131
  if (!EcArray.isArray(this.suggestedAnswer)) {
132
- throw new Error("Suggested Answer is not Array");
132
+ throw new Error("Suggested Answer is not Array"); // NOSONAR - A TypeError would change the name and toString() callers see.
133
133
  }
134
134
  let ary = this.suggestedAnswer;
135
135
  ary.push(answer.id);
@@ -139,7 +139,7 @@ module.exports = class EcQuestion extends schema.Question {
139
139
  return;
140
140
  }
141
141
  if (!EcArray.isArray(this.suggestedAnswer)) {
142
- throw new Error("Suggested Answer is not Array");
142
+ throw new Error("Suggested Answer is not Array"); // NOSONAR - A TypeError would change the name and toString() callers see.
143
143
  }
144
144
  let ary = this.suggestedAnswer;
145
145
  for (let i = 0; i < ary.length; i++) {
@@ -156,7 +156,7 @@ module.exports = class EcQuestion extends schema.Question {
156
156
  return;
157
157
  }
158
158
  if (!EcArray.isArray(this.acceptedAnswer)) {
159
- throw new Error("Accepted Answer is not Array");
159
+ throw new Error("Accepted Answer is not Array"); // NOSONAR - A TypeError would change the name and toString() callers see.
160
160
  }
161
161
  let ary = this.acceptedAnswer;
162
162
  for (let i = 0; i < ary.length; i++) {
@@ -14,7 +14,7 @@ module.exports = class Concept extends EcRemoteLinkedData {
14
14
  super("https://schema.cassproject.org/0.4/skos/", "Concept");
15
15
  }
16
16
 
17
- static TYPE_0_1 = "http://schema.cassproject.org/0.3/skos/Concept";
17
+ static TYPE_0_1 = "http://schema.cassproject.org/0.3/skos/Concept"; // NOSONAR - JSON-LD identifier, never fetched
18
18
  static TYPE_0_2 = "https://schema.cassproject.org/0.3/skos/Concept";
19
19
  static TYPE_0_3 = "https://schema.cassproject.org/0.4/skos/Concept";
20
20
  static myType = this.TYPE_0_3;
@@ -48,10 +48,10 @@ module.exports = class Concept extends EcRemoteLinkedData {
48
48
  }
49
49
  }
50
50
  getTypes() {
51
- let a = [];
52
- a.push(Concept.TYPE_0_3);
53
- a.push(Concept.TYPE_0_2);
54
- a.push(Concept.TYPE_0_1);
55
- return a;
51
+ return [
52
+ Concept.TYPE_0_3,
53
+ Concept.TYPE_0_2,
54
+ Concept.TYPE_0_1,
55
+ ];
56
56
  }
57
57
  };
@@ -14,7 +14,7 @@ module.exports = class ConceptScheme extends EcRemoteLinkedData {
14
14
  constructor() {
15
15
  super("https://schema.cassproject.org/0.4/skos/", "ConceptScheme");
16
16
  }
17
- static TYPE_0_1 = "http://schema.cassproject.org/0.3/skos/ConceptScheme";
17
+ static TYPE_0_1 = "http://schema.cassproject.org/0.3/skos/ConceptScheme"; // NOSONAR - JSON-LD identifier, never fetched
18
18
  static TYPE_0_2 = "https://schema.cassproject.org/0.3/skos/ConceptScheme";
19
19
  static TYPE_0_3 = "https://schema.cassproject.org/0.4/skos/ConceptScheme";
20
20
  static myType = this.TYPE_0_3;
@@ -41,10 +41,10 @@ module.exports = class ConceptScheme extends EcRemoteLinkedData {
41
41
  }
42
42
  }
43
43
  getTypes() {
44
- let a = [];
45
- a.push(ConceptScheme.TYPE_0_3);
46
- a.push(ConceptScheme.TYPE_0_2);
47
- a.push(ConceptScheme.TYPE_0_1);
48
- return a;
44
+ return [
45
+ ConceptScheme.TYPE_0_3,
46
+ ConceptScheme.TYPE_0_2,
47
+ ConceptScheme.TYPE_0_1,
48
+ ];
49
49
  }
50
50
  };
@@ -6,12 +6,11 @@ const EcRepository = require("../../cassproject/ebac/repository/EcRepository");
6
6
  module.exports = class EcConcept extends Concept {
7
7
  constructor() {
8
8
  super();
9
- let me = this;
10
9
  if (EcConcept.template != null) {
11
10
  let you = EcConcept.template;
12
11
  for (let key in you) {
13
12
  if (typeof you[key] != "function")
14
- me[key.replace("@", "")] = you[key];
13
+ this[key.replace("@", "")] = you[key];
15
14
  }
16
15
  }
17
16
  }
@@ -6,12 +6,11 @@ const EcRepository = require("../../cassproject/ebac/repository/EcRepository");
6
6
  module.exports = class EcConceptScheme extends ConceptScheme {
7
7
  constructor() {
8
8
  super();
9
- let me = this;
10
9
  if (EcConceptScheme.template != null) {
11
10
  let you = EcConceptScheme.template;
12
11
  for (let key in you) {
13
12
  if (typeof you[key] != "function")
14
- me[key.replace("@", "")] = you[key];
13
+ this[key.replace("@", "")] = you[key];
15
14
  }
16
15
  }
17
16
  }
@@ -1,5 +1,5 @@
1
1
 
2
- if ((typeof Cypress !== 'undefined') && Cypress != null && Cypress.env != null)
2
+ if ((typeof Cypress !== 'undefined') && Cypress != null)
3
3
  afterEach(() => {
4
4
  if (this.currentTest.state === 'failed') {
5
5
  Cypress.runner.end();
@@ -0,0 +1,172 @@
1
+ // Checks that the parts of chai and sinon these tests rely on still behave as
2
+ // expected, so an upgrade cannot make assertions pass for the wrong reason.
3
+ const chai = require('chai');
4
+ const sinon = require('sinon');
5
+
6
+ const assert = chai.assert;
7
+ const expect = chai.expect;
8
+
9
+ // Runs fn and returns its chai AssertionError, failing if it did not throw one.
10
+ function failure(fn) {
11
+ try {
12
+ fn();
13
+ } catch (e) {
14
+ if (e instanceof chai.AssertionError) return e;
15
+ throw e;
16
+ }
17
+ throw new Error('assertion did not fail');
18
+ }
19
+
20
+ describe('chai', () => {
21
+ it('assert passes and fails as expected', () => {
22
+ assert.equal(1, '1');
23
+ failure(() => assert.equal(1, 2));
24
+ assert.strictEqual(1, 1);
25
+ failure(() => assert.strictEqual(1, '1'));
26
+ assert.deepEqual({ a: [1, { b: 2 }] }, { a: [1, { b: 2 }] });
27
+ failure(() => assert.deepEqual({ a: [1] }, { a: [2] }));
28
+ assert.notEqual(1, 2);
29
+ failure(() => assert.notEqual(1, 1));
30
+ assert.isTrue(true);
31
+ failure(() => assert.isTrue(1));
32
+ assert.isFalse(false);
33
+ failure(() => assert.isFalse(0));
34
+ assert.isNull(null);
35
+ failure(() => assert.isNull(undefined));
36
+ assert.isNotNull(0);
37
+ failure(() => assert.isNotNull(null));
38
+ assert.isUndefined(undefined);
39
+ failure(() => assert.isUndefined(null));
40
+ assert.isDefined(null);
41
+ failure(() => assert.isDefined(undefined));
42
+ assert.isString('s');
43
+ failure(() => assert.isString(1));
44
+ assert.isObject({});
45
+ failure(() => assert.isObject('s'));
46
+ assert.isFunction(() => 0);
47
+ failure(() => assert.isFunction({}));
48
+ assert.isNaN(NaN);
49
+ failure(() => assert.isNaN(1));
50
+ assert.isAbove(2, 1);
51
+ failure(() => assert.isAbove(1, 1));
52
+ assert.isBelow(1, 2);
53
+ failure(() => assert.isBelow(2, 2));
54
+ assert.include([1, 2], 2);
55
+ assert.include('abc', 'b');
56
+ failure(() => assert.include([1, 2], 3));
57
+ assert.match('abc', /b/);
58
+ failure(() => assert.match('abc', /d/));
59
+ assert.lengthOf([1, 2], 2);
60
+ failure(() => assert.lengthOf([1], 2));
61
+ assert.exists(0);
62
+ failure(() => assert.exists(null));
63
+ class A { }
64
+ class B extends A { }
65
+ assert.instanceOf(new B(), A);
66
+ failure(() => assert.instanceOf({}, A));
67
+ assert.notInstanceOf(new A(), B);
68
+ failure(() => assert.notInstanceOf(new B(), A));
69
+ failure(() => assert.fail('message'));
70
+ assert.equal(failure(() => assert.fail('message')).message, 'message');
71
+ });
72
+
73
+ it('expect passes and fails as expected', () => {
74
+ expect(1).to.equal(1);
75
+ failure(() => expect(1).to.equal(2));
76
+ expect({ a: [1] }).to.deep.equal({ a: [1] });
77
+ failure(() => expect({ a: [1] }).to.deep.equal({ a: [2] }));
78
+ expect(true).to.be.true;
79
+ failure(() => expect(1).to.be.true);
80
+ expect(false).to.be.false;
81
+ failure(() => expect(0).to.be.false);
82
+ expect([1]).to.be.an('array').with.lengthOf(1);
83
+ failure(() => expect([1]).to.be.an('array').with.lengthOf(2));
84
+ expect('s').to.be.a('string');
85
+ failure(() => expect(1).to.be.a('string'));
86
+ expect(new Date()).to.be.an.instanceof(Date);
87
+ failure(() => expect({}).to.be.an.instanceof(Date));
88
+ expect('Row 2: x').to.contain('Row 2');
89
+ failure(() => expect('Row 2').to.contain('Row 3'));
90
+ expect([1, 2]).to.include(2);
91
+ failure(() => expect([1, 2]).to.include(3));
92
+ expect([1, 2]).to.not.include(3);
93
+ failure(() => expect([1, 2]).to.not.include(2));
94
+ });
95
+
96
+ it('should is available', () => {
97
+ const should = chai.should();
98
+ (1).should.equal(1);
99
+ failure(() => (1).should.equal(2));
100
+ should.not.exist(null);
101
+ });
102
+ });
103
+
104
+ describe('sinon', () => {
105
+ afterEach(() => sinon.restore());
106
+
107
+ it('spies record calls and arguments', () => {
108
+ const target = { f: (a) => a * 2 };
109
+ const spy = sinon.spy(target, 'f');
110
+ assert.isFalse(spy.called);
111
+ assert.equal(target.f(3), 6);
112
+ assert.isTrue(spy.called);
113
+ assert.isTrue(spy.calledOnce);
114
+ assert.equal(spy.callCount, 1);
115
+ assert.isTrue(spy.calledWith(3));
116
+ assert.isFalse(spy.calledWith(4));
117
+ assert.deepEqual(spy.firstCall.args, [3]);
118
+ assert.isTrue(spy.calledWith(sinon.match.any));
119
+ assert.isTrue(spy.calledWith(sinon.match.number));
120
+ assert.isFalse(spy.calledWith(sinon.match.string));
121
+ });
122
+
123
+ it('stubs resolve, reject, return and call fakes', async () => {
124
+ const target = { a: () => 1, b: () => 1, c: () => 1, d: () => 1 };
125
+ sinon.stub(target, 'a').resolves(5);
126
+ sinon.stub(target, 'b').rejects(new Error('no'));
127
+ sinon.stub(target, 'c').returns(7);
128
+ sinon.stub(target, 'd').callsFake((x) => x + 1);
129
+ assert.equal(await target.a(), 5);
130
+ let error;
131
+ await target.b().catch((e) => { error = e; });
132
+ assert.equal(error.message, 'no');
133
+ assert.equal(target.c(), 7);
134
+ assert.equal(target.d(1), 2);
135
+ });
136
+
137
+ it('stubs can be changed after creation and expose the original', () => {
138
+ const target = { f: () => 'original' };
139
+ const stub = sinon.stub(target, 'f').returns('first');
140
+ assert.equal(target.f(), 'first');
141
+ stub.callsFake(() => 'second');
142
+ assert.equal(target.f(), 'second');
143
+ assert.equal(stub.wrappedMethod(), 'original');
144
+ stub.rejects(new Error('later'));
145
+ return target.f().then(() => assert.fail('should reject'), (e) => assert.equal(e.message, 'later'));
146
+ });
147
+
148
+ it('restores originals, individually and all at once', () => {
149
+ const target = { f: () => 'f', g: () => 'g' };
150
+ const f = sinon.stub(target, 'f').returns('stubbed');
151
+ sinon.stub(target, 'g').returns('stubbed');
152
+ f.restore();
153
+ assert.equal(target.f(), 'f');
154
+ assert.equal(target.g(), 'stubbed');
155
+ sinon.restore();
156
+ assert.equal(target.g(), 'g');
157
+ assert.isUndefined(target.f.restore);
158
+ });
159
+
160
+ it('fake timers freeze the clock and restore it', () => {
161
+ const clock = sinon.useFakeTimers({ now: 1700000000000 });
162
+ try {
163
+ assert.equal(new Date().getTime(), 1700000000000);
164
+ assert.equal(Date.now(), 1700000000000);
165
+ clock.tick(5);
166
+ assert.equal(Date.now(), 1700000000005);
167
+ } finally {
168
+ clock.restore();
169
+ }
170
+ assert.isAbove(Date.now(), 1700000000005);
171
+ });
172
+ });
@@ -0,0 +1,122 @@
1
+ require('../../index.js');
2
+ const chai = require('chai');
3
+ const sinon = require('sinon');
4
+ const EcRepository = require('../org/cassproject/ebac/repository/EcRepository.js');
5
+
6
+ const assert = chai.assert;
7
+
8
+ const SERVER = "http://repo.test/api/";
9
+ const TYPE = "http://www.w3.org/1999/02/22-rdf-syntax-ns#type";
10
+ const TITLE = "http://purl.org/dc/elements/1.1/title";
11
+ const DESC = "http://purl.org/dc/terms/description";
12
+ const CHILD = "http://purl.org/gem/qualifiers/hasChild";
13
+ const STATEMENT = "http://purl.org/ASN/schema/core/Statement";
14
+ const DOCUMENT = "http://purl.org/ASN/schema/core/StandardDocument";
15
+
16
+ function asnDocument() {
17
+ return {
18
+ "http://asn.test/D1": { [TYPE]: [{ value: DOCUMENT }], [TITLE]: [{ value: "Doc" }], [DESC]: [{ value: "Doc desc" }], [CHILD]: [{ value: "http://asn.test/S1" }] },
19
+ "http://asn.test/S1": { [TYPE]: [{ value: STATEMENT }], [DESC]: [{ value: "Statement one" }], [CHILD]: [{ value: "http://asn.test/S2" }, { value: "http://asn.test/S3" }] },
20
+ "http://asn.test/S2": { [TYPE]: [{ value: STATEMENT }], [TITLE]: [{ value: "Two" }] },
21
+ "http://asn.test/S3": { [TYPE]: [{ value: STATEMENT }], [TITLE]: [{ value: "Three" }], [DESC]: [{ value: "Three desc" }] }
22
+ };
23
+ }
24
+
25
+ describe('ASNImport', () => {
26
+ afterEach(() => sinon.restore());
27
+
28
+ beforeEach(() => {
29
+ ASNImport.jsonCompetencies = {};
30
+ ASNImport.jsonFramework = null;
31
+ ASNImport.frameworkUrl = "";
32
+ ASNImport.lookThroughSource(asnDocument());
33
+ });
34
+
35
+ const run = (serverUrl, createFramework, repo) => new Promise((resolve, reject) => {
36
+ const failures = [];
37
+ ASNImport.importCompetencies(serverUrl, null, createFramework, (comps, fw) => resolve({ comps, fw, failures }), (e) => { failures.push(e); reject(e); }, () => { }, repo);
38
+ });
39
+
40
+ it('finds the framework, competencies and relation count', () => {
41
+ assert.equal(ASNImport.frameworkUrl, "http://asn.test/D1");
42
+ assert.deepEqual(Object.keys(ASNImport.jsonCompetencies), ["http://asn.test/S1", "http://asn.test/S2", "http://asn.test/S3"]);
43
+ assert.equal(ASNImport.competencyCount, 3);
44
+ assert.equal(ASNImport.relationCount, 2);
45
+ });
46
+
47
+ it('imports competencies keyed by their ASN ids and narrows relations with versioned ids', async () => {
48
+ const saved = [];
49
+ const repo = { selectedServer: SERVER, saveTo: (o, s) => { saved.push(o); s("ok"); } };
50
+ const { comps, fw } = await run(SERVER, false, repo);
51
+ assert.isNull(fw);
52
+ assert.deepEqual(comps.map(c => [c.id, c.name, c.description]), [
53
+ ["http://asn.test/S1", "Statement one", "Statement one"],
54
+ ["http://asn.test/S2", "Two", undefined],
55
+ ["http://asn.test/S3", "Three", "Three desc"]
56
+ ]);
57
+ const relations = saved.filter(o => o instanceof EcAlignment);
58
+ assert.deepEqual(relations.map(r => [r.source, r.target, r.relationType]).sort(), [
59
+ ["http://asn.test/S2", "http://asn.test/S1", "narrows"],
60
+ ["http://asn.test/S3", "http://asn.test/S1", "narrows"]
61
+ ]);
62
+ for (const r of relations) {
63
+ assert.isTrue(r.id.startsWith(SERVER));
64
+ assert.notEqual(r.id, r.shortId());
65
+ }
66
+ });
67
+
68
+ it('generates short relation ids when the repository server does not contain the server url', async () => {
69
+ const saved = [];
70
+ const repo = { selectedServer: SERVER, saveTo: (o, s) => { saved.push(o); s("ok"); } };
71
+ await run("http://other.test/", false, repo);
72
+ const relations = saved.filter(o => o instanceof EcAlignment);
73
+ assert.lengthOf(relations, 2);
74
+ for (const r of relations) {
75
+ assert.isTrue(r.id.startsWith("http://other.test/"));
76
+ assert.equal(r.id, r.shortId());
77
+ }
78
+ });
79
+
80
+ it('generates versioned relation ids and saves through EcRepository without a repository', async () => {
81
+ const saved = [];
82
+ sinon.stub(EcRepository, 'save').callsFake((o, s) => { saved.push(o); s("ok"); });
83
+ const { fw } = await run(SERVER, true, null);
84
+ const relations = saved.filter(o => o instanceof EcAlignment);
85
+ assert.lengthOf(relations, 2);
86
+ for (const r of relations)
87
+ assert.notEqual(r.id, r.shortId());
88
+ assert.equal(fw.id, "http://asn.test/D1");
89
+ assert.equal(fw.name, "Doc");
90
+ assert.equal(fw.description, "Doc desc");
91
+ assert.deepEqual(fw.competency, ["http://asn.test/S1", "http://asn.test/S2", "http://asn.test/S3"]);
92
+ assert.lengthOf(fw.relation, 2);
93
+ });
94
+
95
+ describe('createFramework without a framework url', () => {
96
+ const create = (serverUrl, repo) => new Promise((resolve, reject) => {
97
+ ASNImport.importedFramework = new EcFramework();
98
+ ASNImport.frameworkUrl = null;
99
+ ASNImport.competencies = {};
100
+ ASNImport.createFramework(serverUrl, null, (comps, fw) => resolve(fw), reject, repo);
101
+ });
102
+
103
+ it('generates a versioned id when the repository server contains the server url', async () => {
104
+ const fw = await create(SERVER, { selectedServer: SERVER, saveTo: (o, s) => s("ok") });
105
+ assert.isTrue(fw.id.startsWith(SERVER));
106
+ assert.notEqual(fw.id, fw.shortId());
107
+ });
108
+
109
+ it('generates a short id when the repository server does not contain the server url', async () => {
110
+ const fw = await create("http://other.test/", { selectedServer: SERVER, saveTo: (o, s) => s("ok") });
111
+ assert.isTrue(fw.id.startsWith("http://other.test/"));
112
+ assert.equal(fw.id, fw.shortId());
113
+ });
114
+
115
+ it('generates a versioned id without a repository', async () => {
116
+ sinon.stub(EcRepository, 'save').callsFake((o, s) => s("ok"));
117
+ const fw = await create(SERVER, null);
118
+ assert.isTrue(fw.id.startsWith(SERVER));
119
+ assert.notEqual(fw.id, fw.shortId());
120
+ });
121
+ });
122
+ });
@@ -0,0 +1,57 @@
1
+ require('../../index.js');
2
+ const chai = require('chai');
3
+
4
+ const assert = chai.assert;
5
+
6
+ const CTX = "https://schema.cassproject.org/0.4";
7
+
8
+ describe('CSVExport.CSVExportProcess', () => {
9
+ const competency = (props) => Object.assign({ "@context": CTX, "@type": "Competency" }, props);
10
+
11
+ it('skips falsy entries when building the export', () => {
12
+ const p = new CSVExport.CSVExportProcess();
13
+ p.buildExport([null, competency({ "@id": "http://x/1", name: "One" }), undefined, 0, "", false, competency({ "@id": "http://x/2", name: "Two" })], false);
14
+ assert.lengthOf(p.csvOutput, 2);
15
+ assert.equal(p.csvOutput[0].name, "One");
16
+ assert.equal(p.csvOutput[1].name, "Two");
17
+ });
18
+
19
+ it('flattens nested objects with dotted names when not piped', () => {
20
+ const p = new CSVExport.CSVExportProcess();
21
+ p.buildExport([competency({ "@id": "http://x/1", name: { "@language": "en", "@value": "One" } })], false);
22
+ assert.equal(p.csvOutput[0]["name.@language"], "en");
23
+ assert.equal(p.csvOutput[0]["name.@value"], "One");
24
+ });
25
+
26
+ it('joins piped arrays and objects with |', () => {
27
+ const p = new CSVExport.CSVExportProcess();
28
+ p.buildExport([competency({
29
+ "@id": "http://x/1",
30
+ "ceasn:keyword": ["a", "b"],
31
+ "ceasn:nested": { first: "x", second: ["y", "z"] }
32
+ })], true);
33
+ assert.equal(p.csvOutput[0]["ceasn:keyword"], "a|b");
34
+ assert.equal(p.csvOutput[0]["ceasn:nested"], "x|y|z");
35
+ });
36
+
37
+ it('formats piped ceterms:versionIdentifier entries as code~type~uri', () => {
38
+ const p = new CSVExport.CSVExportProcess();
39
+ p.buildExport([competency({
40
+ "@id": "http://x/1",
41
+ "ceterms:versionIdentifier": [
42
+ { "ceterms:identifierValueCode": "1.0", "ceterms:identifierTypeName": { "en-us": "Version" }, "ceterms:identifierType": "http://t/1" },
43
+ { "ceterms:identifierValueCode": "2.0" },
44
+ { "ceterms:identifierTypeName": { "en-us": "Only" } }
45
+ ]
46
+ })], true);
47
+ // Entries without an identifierType get no "|" separator; the final character is always dropped.
48
+ assert.equal(p.csvOutput[0]["ceterms:versionIdentifier"], "1.0~Version~http://t/1|2.0~Only");
49
+ });
50
+
51
+ it('fills missing columns with empty strings on earlier rows', () => {
52
+ const p = new CSVExport.CSVExportProcess();
53
+ p.buildExport([competency({ "@id": "http://x/1", name: "One" }), competency({ "@id": "http://x/2", name: "Two", description: "D" })], false);
54
+ assert.strictEqual(p.csvOutput[0].description, "");
55
+ assert.equal(p.csvOutput[1].description, "D");
56
+ });
57
+ });