dceky 1.0.0-beta-profile.1 → 1.0.0-beta-yuenler.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.
- package/lib/commands/assertDoesNotHaveClass.d.ts +19 -0
- package/lib/commands/assertDoesNotHaveClass.js +17 -0
- package/lib/commands/assertDoesNotHaveClass.js.map +1 -0
- package/lib/commands/assertHasClass.d.ts +19 -0
- package/lib/commands/assertHasClass.js +17 -0
- package/lib/commands/assertHasClass.js.map +1 -0
- package/lib/commands/assertNumElements.d.ts +19 -0
- package/lib/commands/assertNumElements.js +17 -0
- package/lib/commands/assertNumElements.js.map +1 -0
- package/lib/commands/clickWithRetry.d.ts +16 -0
- package/lib/commands/clickWithRetry.js +29 -0
- package/lib/commands/clickWithRetry.js.map +1 -0
- package/lib/commands/getNumElements.d.ts +15 -0
- package/lib/commands/getNumElements.js +19 -0
- package/lib/commands/getNumElements.js.map +1 -0
- package/lib/commands/handleHarvardKey.d.ts +14 -0
- package/lib/commands/handleHarvardKey.js +59 -0
- package/lib/commands/handleHarvardKey.js.map +1 -0
- package/lib/commands/launchAs.d.ts +23 -0
- package/lib/commands/launchAs.js +65 -0
- package/lib/commands/launchAs.js.map +1 -0
- package/lib/commands/launchLTIUsingToken.d.ts +16 -0
- package/lib/commands/launchLTIUsingToken.js +57 -0
- package/lib/commands/launchLTIUsingToken.js.map +1 -0
- package/lib/commands/navigateToHref.d.ts +20 -0
- package/lib/commands/navigateToHref.js +23 -0
- package/lib/commands/navigateToHref.js.map +1 -0
- package/lib/commands/runScript.d.ts +17 -0
- package/lib/commands/runScript.js +25 -0
- package/lib/commands/runScript.js.map +1 -0
- package/lib/commands/typeInto.d.ts +21 -0
- package/lib/commands/typeInto.js +28 -0
- package/lib/commands/typeInto.js.map +1 -0
- package/lib/commands/visitCanvasGETEndpoint.d.ts +20 -0
- package/lib/commands/visitCanvasGETEndpoint.js +26 -0
- package/lib/commands/visitCanvasGETEndpoint.js.map +1 -0
- package/lib/commands/waitForElementVisible.d.ts +15 -0
- package/lib/commands/waitForElementVisible.js +20 -0
- package/lib/commands/waitForElementVisible.js.map +1 -0
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/init.d.ts +4 -11
- package/lib/init.js +35 -4
- package/lib/init.js.map +1 -1
- package/package.json +4 -20
- package/src/commands/assertDoesNotHaveClass.ts +40 -0
- package/src/commands/assertHasClass.ts +40 -0
- package/src/commands/assertNumElements.ts +37 -0
- package/src/commands/clickWithRetry.ts +51 -0
- package/src/commands/getNumElements.ts +38 -0
- package/src/commands/handleHarvardKey.ts +87 -0
- package/src/commands/launchAs.ts +104 -0
- package/src/commands/launchLTIUsingToken.ts +84 -0
- package/src/commands/navigateToHref.ts +45 -0
- package/src/commands/runScript.ts +42 -0
- package/src/commands/typeInto.ts +57 -0
- package/src/commands/visitCanvasGETEndpoint.ts +48 -0
- package/src/commands/waitForElementVisible.ts +40 -0
- package/src/genConfiguration.ts +11 -0
- package/src/index.ts +2 -7
- package/src/init.ts +26 -2
- package/tsconfig.json +0 -1
- package/docs/GlobalsAndProfiles.md +0 -23
- package/profileChooser/chooseProfile.ts +0 -35
- package/profileChooser/helpers/print.ts +0 -155
- package/profileChooser/helpers/prompt.ts +0 -23
- package/profileChooser/helpers/showChooser.ts +0 -95
- package/profileChooser/types/ChooserOption.ts +0 -11
- package/src/commands/clickSomething.ts +0 -33
- package/src/genConfiguration/helpers/resolveDependents.ts +0 -47
- package/src/genConfiguration/helpers/splitEnv.ts +0 -30
- package/src/genConfiguration/index.ts +0 -83
- package/src/genConfiguration/types/DependentValue.ts +0 -14
- package/src/genConfiguration/types/GlobalsOrProfile.ts +0 -12
- package/src/genConfiguration/types/SplitEnv.ts +0 -18
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
declare global {
|
|
2
|
+
namespace Cypress {
|
|
3
|
+
interface Chainable {
|
|
4
|
+
/**
|
|
5
|
+
* Makes a GET request to Canvas API endpoint
|
|
6
|
+
* @param opts object containing all arguments
|
|
7
|
+
* @param opts.path The API path (e.g., '/courses', '/users/self')
|
|
8
|
+
* @param opts.accessToken The Canvas access token
|
|
9
|
+
* @returns The API response body
|
|
10
|
+
* @example cy.visitCanvasGETEndpoint({ path: '/courses', accessToken: 'your_token' })
|
|
11
|
+
*/
|
|
12
|
+
visitCanvasGETEndpoint(opts: {
|
|
13
|
+
path: string;
|
|
14
|
+
accessToken: string;
|
|
15
|
+
}): Chainable<any>;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
declare const visitCanvasGETEndpoint: () => void;
|
|
20
|
+
export default visitCanvasGETEndpoint;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/// <reference types="cypress" />
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
/*----------------------------------------*/
|
|
5
|
+
/* --------------- Command -------------- */
|
|
6
|
+
/*----------------------------------------*/
|
|
7
|
+
var visitCanvasGETEndpoint = function () {
|
|
8
|
+
Cypress.Commands.add('visitCanvasGETEndpoint', function (opts) {
|
|
9
|
+
cy.log('Visiting Canvas GET endpoint');
|
|
10
|
+
var path = opts.path, accessToken = opts.accessToken;
|
|
11
|
+
cy.log('Canvas API: ' + path);
|
|
12
|
+
return cy.request({
|
|
13
|
+
method: 'GET',
|
|
14
|
+
url: 'https://canvas.harvard.edu/api/v1' + path,
|
|
15
|
+
qs: {
|
|
16
|
+
per_page: 200,
|
|
17
|
+
access_token: accessToken
|
|
18
|
+
}
|
|
19
|
+
}).its('body');
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
/*----------------------------------------*/
|
|
23
|
+
/* --------------- Export --------------- */
|
|
24
|
+
/*----------------------------------------*/
|
|
25
|
+
exports.default = visitCanvasGETEndpoint;
|
|
26
|
+
//# sourceMappingURL=visitCanvasGETEndpoint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"visitCanvasGETEndpoint.js","sourceRoot":"","sources":["../../src/commands/visitCanvasGETEndpoint.ts"],"names":[],"mappings":";AAAA,iCAAiC;;AAsBjC,4CAA4C;AAC5C,4CAA4C;AAC5C,4CAA4C;AAE5C,IAAM,sBAAsB,GAAG;IAC7B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,wBAAwB,EAAE,UAAC,IAA2C;QACzF,EAAE,CAAC,GAAG,CAAC,8BAA8B,CAAC,CAAC;QAC/B,IAAA,IAAI,GAAkB,IAAI,KAAtB,EAAE,WAAW,GAAK,IAAI,YAAT,CAAU;QAEnC,EAAE,CAAC,GAAG,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;QAC9B,OAAO,EAAE,CAAC,OAAO,CAAC;YAChB,MAAM,EAAE,KAAK;YACb,GAAG,EAAE,mCAAmC,GAAG,IAAI;YAC/C,EAAE,EAAE;gBACF,QAAQ,EAAE,GAAG;gBACb,YAAY,EAAE,WAAW;aAC1B;SACF,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACjB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC;AAEF,4CAA4C;AAC5C,4CAA4C;AAC5C,4CAA4C;AAE5C,kBAAe,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
declare global {
|
|
2
|
+
namespace Cypress {
|
|
3
|
+
interface Chainable {
|
|
4
|
+
/**
|
|
5
|
+
* Wait for an element to be visible
|
|
6
|
+
* @param item the CSS selector of interest
|
|
7
|
+
* @param timeoutSec the number of seconds to wait before timing out (default: 10)
|
|
8
|
+
* @example cy.waitForElementVisible('#submit-button', 15)
|
|
9
|
+
*/
|
|
10
|
+
waitForElementVisible(item: string, timeoutSec?: number): Chainable<JQuery<HTMLElement>>;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
declare const waitForElementVisible: () => void;
|
|
15
|
+
export default waitForElementVisible;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/// <reference types="cypress" />
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
/*----------------------------------------*/
|
|
5
|
+
/* --------------- Command -------------- */
|
|
6
|
+
/*----------------------------------------*/
|
|
7
|
+
var waitForElementVisible = function () {
|
|
8
|
+
Cypress.Commands.add('waitForElementVisible', function (item, timeoutSec) {
|
|
9
|
+
cy.log('Waiting for element to be visible');
|
|
10
|
+
timeoutSec = timeoutSec || 10;
|
|
11
|
+
var timeoutMs = timeoutSec * 1000;
|
|
12
|
+
cy.log("Wait for ".concat(item, " to be visible"));
|
|
13
|
+
return cy.get(item, { timeout: timeoutMs }).should('be.visible');
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
/*----------------------------------------*/
|
|
17
|
+
/* --------------- Export --------------- */
|
|
18
|
+
/*----------------------------------------*/
|
|
19
|
+
exports.default = waitForElementVisible;
|
|
20
|
+
//# sourceMappingURL=waitForElementVisible.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"waitForElementVisible.js","sourceRoot":"","sources":["../../src/commands/waitForElementVisible.ts"],"names":[],"mappings":";AAAA,iCAAiC;;AAoBjC,4CAA4C;AAC5C,4CAA4C;AAC5C,4CAA4C;AAE5C,IAAM,qBAAqB,GAAG;IAC5B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,uBAAuB,EAAE,UAAC,IAAY,EAAE,UAAmB;QAC9E,EAAE,CAAC,GAAG,CAAC,mCAAmC,CAAC,CAAC;QAC5C,UAAU,GAAG,UAAU,IAAI,EAAE,CAAC;QAE9B,IAAM,SAAS,GAAG,UAAU,GAAG,IAAI,CAAC;QACpC,EAAE,CAAC,GAAG,CAAC,mBAAY,IAAI,mBAAgB,CAAC,CAAC;QACzC,OAAO,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;IACnE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC;AAEF,4CAA4C;AAC5C,4CAA4C;AAC5C,4CAA4C;AAE5C,kBAAe,qBAAqB,CAAC"}
|
package/lib/index.js
CHANGED
|
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.init = exports.genConfiguration = void 0;
|
|
7
|
+
// Import helpers
|
|
7
8
|
var init_1 = __importDefault(require("./init"));
|
|
8
9
|
exports.init = init_1.default;
|
|
9
10
|
var genConfiguration_1 = __importDefault(require("./genConfiguration"));
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;AAAA,gDAA0B;AASxB,eATK,cAAI,CASL;AARN,wEAAkD;AAOhD,2BAPK,0BAAgB,CAOL;AALlB,sDAAsD;AACtD,IAAA,cAAI,GAAE,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;AAAA,iBAAiB;AACjB,gDAA0B;AASxB,eATK,cAAI,CASL;AARN,wEAAkD;AAOhD,2BAPK,0BAAgB,CAOL;AALlB,sDAAsD;AACtD,IAAA,cAAI,GAAE,CAAC"}
|
package/lib/init.d.ts
CHANGED
|
@@ -1,13 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
* Custom command to click an element by selector
|
|
6
|
-
* @example cy.clickSomething('.my-button')
|
|
7
|
-
*/
|
|
8
|
-
clickSomething(selector: string): Chainable<Element>;
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Initialize custom commands
|
|
3
|
+
* @author Gabe Abrams
|
|
4
|
+
*/
|
|
12
5
|
declare const init: () => void;
|
|
13
6
|
export default init;
|
package/lib/init.js
CHANGED
|
@@ -1,10 +1,41 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
3
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
// Import all commands
|
|
7
|
+
var assertDoesNotHaveClass_1 = __importDefault(require("./commands/assertDoesNotHaveClass"));
|
|
8
|
+
var assertHasClass_1 = __importDefault(require("./commands/assertHasClass"));
|
|
9
|
+
var assertNumElements_1 = __importDefault(require("./commands/assertNumElements"));
|
|
10
|
+
var clickWithRetry_1 = __importDefault(require("./commands/clickWithRetry"));
|
|
11
|
+
var getNumElements_1 = __importDefault(require("./commands/getNumElements"));
|
|
12
|
+
var handleHarvardKey_1 = __importDefault(require("./commands/handleHarvardKey"));
|
|
13
|
+
var launchAs_1 = __importDefault(require("./commands/launchAs"));
|
|
14
|
+
var launchLTIUsingToken_1 = __importDefault(require("./commands/launchLTIUsingToken"));
|
|
15
|
+
var navigateToHref_1 = __importDefault(require("./commands/navigateToHref"));
|
|
16
|
+
var runScript_1 = __importDefault(require("./commands/runScript"));
|
|
17
|
+
var typeInto_1 = __importDefault(require("./commands/typeInto"));
|
|
18
|
+
var visitCanvasGETEndpoint_1 = __importDefault(require("./commands/visitCanvasGETEndpoint"));
|
|
19
|
+
var waitForElementVisible_1 = __importDefault(require("./commands/waitForElementVisible"));
|
|
20
|
+
/**
|
|
21
|
+
* Initialize custom commands
|
|
22
|
+
* @author Gabe Abrams
|
|
23
|
+
*/
|
|
4
24
|
var init = function () {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
25
|
+
// Execute each command adder
|
|
26
|
+
(0, assertDoesNotHaveClass_1.default)();
|
|
27
|
+
(0, assertHasClass_1.default)();
|
|
28
|
+
(0, assertNumElements_1.default)();
|
|
29
|
+
(0, clickWithRetry_1.default)();
|
|
30
|
+
(0, getNumElements_1.default)();
|
|
31
|
+
(0, handleHarvardKey_1.default)();
|
|
32
|
+
(0, launchAs_1.default)();
|
|
33
|
+
(0, launchLTIUsingToken_1.default)();
|
|
34
|
+
(0, navigateToHref_1.default)();
|
|
35
|
+
(0, runScript_1.default)();
|
|
36
|
+
(0, typeInto_1.default)();
|
|
37
|
+
(0, visitCanvasGETEndpoint_1.default)();
|
|
38
|
+
(0, waitForElementVisible_1.default)();
|
|
8
39
|
};
|
|
9
40
|
exports.default = init;
|
|
10
41
|
//# sourceMappingURL=init.js.map
|
package/lib/init.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":";;;;;AAAA,sBAAsB;AACtB,6FAAuE;AACvE,6EAAuD;AACvD,mFAA6D;AAC7D,6EAAuD;AACvD,6EAAuD;AACvD,iFAA2D;AAC3D,iEAA2C;AAC3C,uFAAiE;AACjE,6EAAuD;AACvD,mEAA6C;AAC7C,iEAA2C;AAC3C,6FAAuE;AACvE,2FAAqE;AAErE;;;GAGG;AACH,IAAM,IAAI,GAAG;IACX,6BAA6B;IAC7B,IAAA,gCAAsB,GAAE,CAAC;IACzB,IAAA,wBAAc,GAAE,CAAC;IACjB,IAAA,2BAAiB,GAAE,CAAC;IACpB,IAAA,wBAAc,GAAE,CAAC;IACjB,IAAA,wBAAc,GAAE,CAAC;IACjB,IAAA,0BAAgB,GAAE,CAAC;IACnB,IAAA,kBAAQ,GAAE,CAAC;IACX,IAAA,6BAAmB,GAAE,CAAC;IACtB,IAAA,wBAAc,GAAE,CAAC;IACjB,IAAA,mBAAS,GAAE,CAAC;IACZ,IAAA,kBAAQ,GAAE,CAAC;IACX,IAAA,gCAAsB,GAAE,CAAC;IACzB,IAAA,+BAAqB,GAAE,CAAC;AAC1B,CAAC,CAAC;AAEF,kBAAe,IAAI,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,17 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dceky",
|
|
3
|
-
"version": "1.0.0-beta-
|
|
3
|
+
"version": "1.0.0-beta-yuenler.1",
|
|
4
4
|
"description": "Cypress toolkit for Harvard DCE",
|
|
5
5
|
"main": "./lib/index.js",
|
|
6
6
|
"types": "./lib/index.d.ts",
|
|
7
7
|
"scripts": {
|
|
8
|
-
"build": "tsc --project ./tsconfig.json"
|
|
9
|
-
"cypress": "npx cypress open",
|
|
10
|
-
"cy:open:stage": "cross-env CYPRESS_PROFILE=stage npx cypress open",
|
|
11
|
-
"cy:open:prod": "cross-env CYPRESS_PROFILE=prod npx cypress open",
|
|
12
|
-
"cy:run:stage": "cross-env CYPRESS_PROFILE=stage npx cypress run",
|
|
13
|
-
"cy:run:prod": "cross-env CYPRESS_PROFILE=prod npx cypress run",
|
|
14
|
-
"launch-cypress": "npx tsx ./profileChooser/chooseProfile.ts"
|
|
8
|
+
"build": "tsc --project ./tsconfig.json"
|
|
15
9
|
},
|
|
16
10
|
"repository": {
|
|
17
11
|
"type": "git",
|
|
@@ -28,21 +22,11 @@
|
|
|
28
22
|
"url": "https://github.com/harvard-edtech/dceky/issues"
|
|
29
23
|
},
|
|
30
24
|
"homepage": "https://github.com/harvard-edtech/dceky#readme",
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"clear": "^0.1.0",
|
|
34
|
-
"prompt-sync": "^4.2.0"
|
|
25
|
+
"peerDependencies": {
|
|
26
|
+
"cypress": "x.x.x"
|
|
35
27
|
},
|
|
36
28
|
"devDependencies": {
|
|
37
|
-
"@types/deepmerge": "2.1.0",
|
|
38
|
-
"@types/node": "^24.6.1",
|
|
39
|
-
"typescript": "5.9.3"
|
|
40
|
-
},
|
|
41
|
-
"peerDependencies": {
|
|
42
|
-
"cypress": "14.5.4",
|
|
43
|
-
"@types/clear": "^0.1.4",
|
|
44
29
|
"@types/node": "^22.7.0",
|
|
45
|
-
"@types/prompt-sync": "^4.2.3",
|
|
46
30
|
"typescript": "^5.9.3"
|
|
47
31
|
}
|
|
48
32
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/// <reference types="cypress" />
|
|
2
|
+
|
|
3
|
+
/*----------------------------------------*/
|
|
4
|
+
/* ---------------- Type ---------------- */
|
|
5
|
+
/*----------------------------------------*/
|
|
6
|
+
|
|
7
|
+
declare global {
|
|
8
|
+
namespace Cypress {
|
|
9
|
+
interface Chainable {
|
|
10
|
+
/**
|
|
11
|
+
* Assert that an element does not have a specific className
|
|
12
|
+
* @param opts object containing all arguments
|
|
13
|
+
* @param opts.item the css selector for finding the element
|
|
14
|
+
* @param opts.className the className to check for
|
|
15
|
+
* @example cy.assertDoesNotHaveClass({ item: '.my-button', className: 'disabled' })
|
|
16
|
+
*/
|
|
17
|
+
assertDoesNotHaveClass(opts: {
|
|
18
|
+
item: string;
|
|
19
|
+
className: string;
|
|
20
|
+
}): Chainable<JQuery<HTMLElement>>;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/*----------------------------------------*/
|
|
26
|
+
/* --------------- Command -------------- */
|
|
27
|
+
/*----------------------------------------*/
|
|
28
|
+
|
|
29
|
+
const assertDoesNotHaveClass = () => {
|
|
30
|
+
Cypress.Commands.add('assertDoesNotHaveClass', (opts: { item: string; className: string }) => {
|
|
31
|
+
cy.log(`Assert ${opts.item} does not have class ${opts.className}`);
|
|
32
|
+
return cy.get(opts.item).should('not.have.class', opts.className);
|
|
33
|
+
});
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/*----------------------------------------*/
|
|
37
|
+
/* --------------- Export --------------- */
|
|
38
|
+
/*----------------------------------------*/
|
|
39
|
+
|
|
40
|
+
export default assertDoesNotHaveClass;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/// <reference types="cypress" />
|
|
2
|
+
|
|
3
|
+
/*----------------------------------------*/
|
|
4
|
+
/* ---------------- Type ---------------- */
|
|
5
|
+
/*----------------------------------------*/
|
|
6
|
+
|
|
7
|
+
declare global {
|
|
8
|
+
namespace Cypress {
|
|
9
|
+
interface Chainable {
|
|
10
|
+
/**
|
|
11
|
+
* Assert that an element has a specific className
|
|
12
|
+
* @param opts object containing all arguments
|
|
13
|
+
* @param opts.item the css selector for finding the element
|
|
14
|
+
* @param opts.className the className to check for
|
|
15
|
+
* @example cy.assertHasClass({ item: '.my-button', className: 'active' })
|
|
16
|
+
*/
|
|
17
|
+
assertHasClass(opts: {
|
|
18
|
+
item: string;
|
|
19
|
+
className: string;
|
|
20
|
+
}): Chainable<JQuery<HTMLElement>>;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/*----------------------------------------*/
|
|
26
|
+
/* --------------- Command -------------- */
|
|
27
|
+
/*----------------------------------------*/
|
|
28
|
+
|
|
29
|
+
const assertHasClass = () => {
|
|
30
|
+
Cypress.Commands.add('assertHasClass', (opts: { item: string; className: string }) => {
|
|
31
|
+
cy.log(`Assert ${opts.item} has class ${opts.className}`);
|
|
32
|
+
return cy.get(opts.item).should('have.class', opts.className);
|
|
33
|
+
});
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/*----------------------------------------*/
|
|
37
|
+
/* --------------- Export --------------- */
|
|
38
|
+
/*----------------------------------------*/
|
|
39
|
+
|
|
40
|
+
export default assertHasClass;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/// <reference types="cypress" />
|
|
2
|
+
|
|
3
|
+
/*----------------------------------------*/
|
|
4
|
+
/* ---------------- Type ---------------- */
|
|
5
|
+
/*----------------------------------------*/
|
|
6
|
+
|
|
7
|
+
declare global {
|
|
8
|
+
namespace Cypress {
|
|
9
|
+
interface Chainable {
|
|
10
|
+
/**
|
|
11
|
+
* Assert a certain number of elements
|
|
12
|
+
* @param opts object containing all arguments
|
|
13
|
+
* @param opts.item a CSS selector corresponding to the item
|
|
14
|
+
* @param opts.num the precise number of elements expected
|
|
15
|
+
* @example cy.assertNumElements({ item: '.list-item', num: 5 })
|
|
16
|
+
*/
|
|
17
|
+
assertNumElements(opts: { item: string; num: number }): Chainable<Element>;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/*----------------------------------------*/
|
|
23
|
+
/* --------------- Command -------------- */
|
|
24
|
+
/*----------------------------------------*/
|
|
25
|
+
|
|
26
|
+
const assertNumElements = () => {
|
|
27
|
+
Cypress.Commands.add('assertNumElements', (opts: { item: string; num: number }) => {
|
|
28
|
+
cy.log(`Assert ${opts.num} elements match ${opts.item}`);
|
|
29
|
+
cy.get(opts.item).should('have.length', opts.num);
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/*----------------------------------------*/
|
|
34
|
+
/* --------------- Export --------------- */
|
|
35
|
+
/*----------------------------------------*/
|
|
36
|
+
|
|
37
|
+
export default assertNumElements;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/// <reference types="cypress" />
|
|
2
|
+
|
|
3
|
+
/*----------------------------------------*/
|
|
4
|
+
/* ---------------- Type ---------------- */
|
|
5
|
+
/*----------------------------------------*/
|
|
6
|
+
|
|
7
|
+
declare global {
|
|
8
|
+
namespace Cypress {
|
|
9
|
+
interface Chainable {
|
|
10
|
+
/**
|
|
11
|
+
* Click an element with visibility checking, scrolling, and timeout handling
|
|
12
|
+
* @param item the CSS selector of interest
|
|
13
|
+
* @param timeoutSec the number of seconds to wait before timing out (default: 10)
|
|
14
|
+
* @param dontScrollTo if true, do not scroll to the element (default: false)
|
|
15
|
+
* @example cy.clickWithRetry('.submit-button', 15, false)
|
|
16
|
+
*/
|
|
17
|
+
clickWithRetry(item: string, timeoutSec?: number, dontScrollTo?: boolean): Chainable<JQuery<HTMLElement>>;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/*----------------------------------------*/
|
|
23
|
+
/* --------------- Command -------------- */
|
|
24
|
+
/*----------------------------------------*/
|
|
25
|
+
|
|
26
|
+
const clickWithRetry = () => {
|
|
27
|
+
Cypress.Commands.add('clickWithRetry', (item: string, timeoutSec?: number, dontScrollTo?: boolean) => {
|
|
28
|
+
timeoutSec = timeoutSec || 10;
|
|
29
|
+
dontScrollTo = dontScrollTo || false;
|
|
30
|
+
|
|
31
|
+
cy.log(`Click ${item}`);
|
|
32
|
+
|
|
33
|
+
// Wait for element to be visible first using our own function
|
|
34
|
+
cy.waitForElementVisible(item, timeoutSec);
|
|
35
|
+
|
|
36
|
+
// If scrolling is disabled, try to click directly
|
|
37
|
+
if (dontScrollTo) {
|
|
38
|
+
return cy.get(item).click();
|
|
39
|
+
} else {
|
|
40
|
+
// Scroll to element and then click using our own function
|
|
41
|
+
cy.get(item).scrollIntoView();
|
|
42
|
+
return cy.get(item).click();
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/*----------------------------------------*/
|
|
48
|
+
/* --------------- Export --------------- */
|
|
49
|
+
/*----------------------------------------*/
|
|
50
|
+
|
|
51
|
+
export default clickWithRetry;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/// <reference types="cypress" />
|
|
2
|
+
|
|
3
|
+
/*----------------------------------------*/
|
|
4
|
+
/* ---------------- Type ---------------- */
|
|
5
|
+
/*----------------------------------------*/
|
|
6
|
+
|
|
7
|
+
declare global {
|
|
8
|
+
namespace Cypress {
|
|
9
|
+
interface Chainable {
|
|
10
|
+
/**
|
|
11
|
+
* Get number of elements
|
|
12
|
+
* @param item a CSS selector corresponding to the item
|
|
13
|
+
* @returns the number of elements on the page
|
|
14
|
+
* @example cy.getNumElements('.list-item').then((count) => { ... })
|
|
15
|
+
*/
|
|
16
|
+
getNumElements(item: string): Chainable<JQuery<any>>;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/*----------------------------------------*/
|
|
22
|
+
/* --------------- Command -------------- */
|
|
23
|
+
/*----------------------------------------*/
|
|
24
|
+
|
|
25
|
+
const getNumElements = () => {
|
|
26
|
+
Cypress.Commands.add('getNumElements', (selector: string) => {
|
|
27
|
+
cy.log(`Count elements matching ${selector}`);
|
|
28
|
+
return cy.get(selector).then(($elements: any) => {
|
|
29
|
+
return cy.wrap($elements.length);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/*----------------------------------------*/
|
|
35
|
+
/* --------------- Export --------------- */
|
|
36
|
+
/*----------------------------------------*/
|
|
37
|
+
|
|
38
|
+
export default getNumElements;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/// <reference types="cypress" />
|
|
2
|
+
|
|
3
|
+
/*----------------------------------------*/
|
|
4
|
+
/* ---------------- Type ---------------- */
|
|
5
|
+
/*----------------------------------------*/
|
|
6
|
+
|
|
7
|
+
declare global {
|
|
8
|
+
namespace Cypress {
|
|
9
|
+
interface Chainable {
|
|
10
|
+
/**
|
|
11
|
+
* Handle a HarvardKey login page for a user
|
|
12
|
+
* @param url the URL to visit for HarvardKey authentication
|
|
13
|
+
* @example cy.handleHarvardKey('https://canvas.harvard.edu/login/saml')
|
|
14
|
+
*/
|
|
15
|
+
handleHarvardKey(url: string): Chainable<void>;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/*----------------------------------------*/
|
|
21
|
+
/* --------------- Command -------------- */
|
|
22
|
+
/*----------------------------------------*/
|
|
23
|
+
|
|
24
|
+
const handleHarvardKey = () => {
|
|
25
|
+
Cypress.Commands.add('handleHarvardKey', (url: string) => {
|
|
26
|
+
cy.log('Handling HarvardKey authentication');
|
|
27
|
+
|
|
28
|
+
// Get credentials from environment variables
|
|
29
|
+
const username = (window as any).Cypress.env('HARVARD_USERNAME');
|
|
30
|
+
const password = (window as any).Cypress.env('HARVARD_PASSWORD');
|
|
31
|
+
|
|
32
|
+
if (!username) {
|
|
33
|
+
throw new Error(`HARVARD_USERNAME environment variable is not set`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!password) {
|
|
37
|
+
throw new Error(`HARVARD_PASSWORD environment variable is not set`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
cy.visit(url);
|
|
41
|
+
|
|
42
|
+
// Get the Harvard login URL using originWithKaixa with auto-initialized functions
|
|
43
|
+
cy.origin('https://apps.cirrusidentity.com', () => {
|
|
44
|
+
Cypress.require('dceky');
|
|
45
|
+
|
|
46
|
+
return cy.navigateToHref({
|
|
47
|
+
item: '#idp_1001962798_button',
|
|
48
|
+
domain: 'https://apps.cirrusidentity.com'
|
|
49
|
+
});
|
|
50
|
+
}).then((fullUrl: any) => {
|
|
51
|
+
// Navigate to the Harvard login page using originWithKaixa
|
|
52
|
+
cy.origin('https://harvard.idp.cirrusidentity.com', {
|
|
53
|
+
args: { username, password, fullUrl }
|
|
54
|
+
}, ({ args }: any) => {
|
|
55
|
+
Cypress.require('dceky');
|
|
56
|
+
const { username, password, fullUrl } = args;
|
|
57
|
+
|
|
58
|
+
cy.on('uncaught:exception', (e: any) => {
|
|
59
|
+
// Ignore known HarvardKey errors
|
|
60
|
+
if (!e.message.includes('ready is not defined')) {
|
|
61
|
+
throw e;
|
|
62
|
+
}
|
|
63
|
+
return false;
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
cy.visit(fullUrl.toString());
|
|
67
|
+
|
|
68
|
+
// Wait for and fill in the login form using enhanced functions
|
|
69
|
+
cy.waitForElementVisible('#username');
|
|
70
|
+
cy.waitForElementVisible('#password');
|
|
71
|
+
|
|
72
|
+
// Add credentials using enhanced functions
|
|
73
|
+
cy.typeInto({ item: '#username', text: username });
|
|
74
|
+
cy.typeInto({ item: '#password', text: password });
|
|
75
|
+
|
|
76
|
+
// Submit the form using clickWithRetry
|
|
77
|
+
cy.clickWithRetry('input[type="submit"], button[type="submit"], .btn-primary');
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/*----------------------------------------*/
|
|
84
|
+
/* --------------- Export --------------- */
|
|
85
|
+
/*----------------------------------------*/
|
|
86
|
+
|
|
87
|
+
export default handleHarvardKey;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/// <reference types="cypress" />
|
|
2
|
+
|
|
3
|
+
/*----------------------------------------*/
|
|
4
|
+
/* ---------------- Type ---------------- */
|
|
5
|
+
/*----------------------------------------*/
|
|
6
|
+
|
|
7
|
+
declare global {
|
|
8
|
+
namespace Cypress {
|
|
9
|
+
interface Chainable {
|
|
10
|
+
/**
|
|
11
|
+
* Log into Canvas and launch an LTI app as a specific user from environment variables.
|
|
12
|
+
* The user should be defined as environment variables with the following properties:
|
|
13
|
+
* - {name}_ACCESS_TOKEN: Canvas access token for the user
|
|
14
|
+
* - {name}_USERNAME: Username (if no access token)
|
|
15
|
+
* - {name}_PASSWORD: Password (if using username)
|
|
16
|
+
* - {name}_IS_XID: Set to 'true' if using XID login
|
|
17
|
+
* - {name}_TYPE: User type ('student', 'ta', 'teacher') for local mode
|
|
18
|
+
* - {name}_SIM_INDEX: Simulator index for local mode
|
|
19
|
+
* @param name the name of the user (used as prefix for environment variables)
|
|
20
|
+
* @param courseId the Canvas ID of the course to launch from (uses COURSE_ID env var if not provided)
|
|
21
|
+
* @param appName the name of the app as it appears in the course's left-hand nav (uses APP_NAME env var if not provided)
|
|
22
|
+
* @example cy.launchAs('student1', 12345, 'My LTI App')
|
|
23
|
+
*/
|
|
24
|
+
launchAs(name: string, courseId?: number, appName?: string): Chainable<void>;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/*----------------------------------------*/
|
|
30
|
+
/* --------------- Command -------------- */
|
|
31
|
+
/*----------------------------------------*/
|
|
32
|
+
|
|
33
|
+
const launchAs = () => {
|
|
34
|
+
Cypress.Commands.add('launchAs', (name: string, courseId?: number, appName?: string) => {
|
|
35
|
+
cy.log(`🚀 Launch as ${name}`);
|
|
36
|
+
|
|
37
|
+
// Get default values from environment variables if not provided
|
|
38
|
+
if (!courseId) {
|
|
39
|
+
const envCourseId = Cypress.env('COURSE_ID');
|
|
40
|
+
if (!envCourseId) {
|
|
41
|
+
throw new Error('courseId parameter is required or COURSE_ID environment variable must be set');
|
|
42
|
+
}
|
|
43
|
+
courseId = parseInt(envCourseId, 10);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (!appName) {
|
|
47
|
+
appName = Cypress.env('APP_NAME');
|
|
48
|
+
if (!appName) {
|
|
49
|
+
throw new Error('appName parameter is required or APP_NAME environment variable must be set');
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Check if this is a local launch
|
|
54
|
+
const isLocal = Cypress.env('LOCAL') === 'true';
|
|
55
|
+
|
|
56
|
+
if (isLocal) {
|
|
57
|
+
// Handle local simulator launch
|
|
58
|
+
const userType = Cypress.env(`${name}_TYPE`) || 'teacher';
|
|
59
|
+
const simIndex = Cypress.env(`${name}_SIM_INDEX`) || '0';
|
|
60
|
+
const simLaunchButtonId = `${userType}_${simIndex}-launch-button`;
|
|
61
|
+
|
|
62
|
+
cy.log('Local mode: launching simulator');
|
|
63
|
+
cy.visit('https://localhost:8088/simulator');
|
|
64
|
+
|
|
65
|
+
// Handle potential SSL certificate issues
|
|
66
|
+
cy.get('body').then(($body) => {
|
|
67
|
+
if ($body.find('.ssl').length > 0) {
|
|
68
|
+
cy.log('Handling SSL certificate issue');
|
|
69
|
+
cy.get('#details-button').click();
|
|
70
|
+
cy.get('#proceed-link').click();
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Launch the app
|
|
75
|
+
cy.get(`#${simLaunchButtonId}`).click();
|
|
76
|
+
|
|
77
|
+
// Check for authorization screen
|
|
78
|
+
cy.wait(1000);
|
|
79
|
+
cy.get('body').then(($body) => {
|
|
80
|
+
if ($body.find('.authorize-button').length > 0) {
|
|
81
|
+
cy.get('.authorize-button').click();
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Handle production launch with access token
|
|
89
|
+
const accessToken = Cypress.env(`${name}_ACCESS_TOKEN`);
|
|
90
|
+
|
|
91
|
+
if (!accessToken) {
|
|
92
|
+
throw new Error(`${name}_ACCESS_TOKEN environment variable is required for user "${name}"`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Launch using the access token
|
|
96
|
+
cy.launchLTIUsingToken(accessToken, courseId, appName);
|
|
97
|
+
});
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
/*----------------------------------------*/
|
|
101
|
+
/* --------------- Export --------------- */
|
|
102
|
+
/*----------------------------------------*/
|
|
103
|
+
|
|
104
|
+
export default launchAs;
|