dceky 1.0.0-beta.6 → 1.0.2

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 (199) hide show
  1. package/.eslintrc.js +93 -0
  2. package/README.md +41 -0
  3. package/cypress/e2e/profile-test.cy.ts +10 -0
  4. package/cypress/fixtures/example.json +5 -0
  5. package/cypress/support/commands.ts +37 -0
  6. package/cypress/support/e2e.ts +17 -0
  7. package/cypress.config.ts +9 -0
  8. package/docs/GlobalsAndProfiles.md +23 -0
  9. package/lib/setup/addToGitIgnore.d.ts +6 -0
  10. package/lib/setup/addToGitIgnore.js +46 -0
  11. package/lib/setup/addToGitIgnore.js.map +1 -0
  12. package/lib/setup/checkRequiredFiles.d.ts +6 -0
  13. package/lib/setup/checkRequiredFiles.js +58 -0
  14. package/lib/setup/checkRequiredFiles.js.map +1 -0
  15. package/lib/setup/genCommandImportFile.d.ts +7 -0
  16. package/lib/setup/genCommandImportFile.js +66 -0
  17. package/lib/setup/genCommandImportFile.js.map +1 -0
  18. package/lib/setup/genConfiguration/helpers/resolveDependents.d.ts +13 -0
  19. package/lib/setup/genConfiguration/helpers/resolveDependents.js +50 -0
  20. package/lib/setup/genConfiguration/helpers/resolveDependents.js.map +1 -0
  21. package/lib/setup/genConfiguration/helpers/splitEnv.d.ts +10 -0
  22. package/lib/setup/genConfiguration/helpers/splitEnv.js +27 -0
  23. package/lib/setup/genConfiguration/helpers/splitEnv.js.map +1 -0
  24. package/lib/setup/genConfiguration/index.d.ts +15 -0
  25. package/lib/setup/genConfiguration/index.js +86 -0
  26. package/lib/setup/genConfiguration/index.js.map +1 -0
  27. package/lib/setup/genConfiguration/types/DependentValue.d.ts +11 -0
  28. package/lib/setup/genConfiguration/types/DependentValue.js +3 -0
  29. package/lib/setup/genConfiguration/types/DependentValue.js.map +1 -0
  30. package/lib/setup/genConfiguration/types/GlobalsOrProfile.d.ts +9 -0
  31. package/lib/setup/genConfiguration/types/GlobalsOrProfile.js +3 -0
  32. package/lib/setup/genConfiguration/types/GlobalsOrProfile.js.map +1 -0
  33. package/lib/setup/genConfiguration/types/SplitEnv.d.ts +13 -0
  34. package/lib/setup/genConfiguration/types/SplitEnv.js +3 -0
  35. package/lib/setup/genConfiguration/types/SplitEnv.js.map +1 -0
  36. package/lib/setup/genConfigurationFile.d.ts +2 -0
  37. package/lib/setup/genConfigurationFile.js +63 -0
  38. package/lib/setup/genConfigurationFile.js.map +1 -0
  39. package/lib/setup/genDynamicConfigFile.d.ts +5 -0
  40. package/lib/setup/genDynamicConfigFile.js +28 -0
  41. package/lib/setup/genDynamicConfigFile.js.map +1 -0
  42. package/lib/setup/index.d.ts +1 -0
  43. package/lib/setup/index.js +16 -0
  44. package/lib/setup/index.js.map +1 -0
  45. package/lib/setup/setupCypressDependencies.d.ts +6 -0
  46. package/lib/setup/setupCypressDependencies.js +40 -0
  47. package/lib/setup/setupCypressDependencies.js.map +1 -0
  48. package/lib/src/commands/assertDoesNotHaveClass.d.ts +20 -0
  49. package/lib/src/commands/assertDoesNotHaveClass.js +17 -0
  50. package/lib/src/commands/assertDoesNotHaveClass.js.map +1 -0
  51. package/lib/src/commands/assertHasClass.d.ts +20 -0
  52. package/lib/src/commands/assertHasClass.js +17 -0
  53. package/lib/src/commands/assertHasClass.js.map +1 -0
  54. package/lib/src/commands/assertNumElements.d.ts +19 -0
  55. package/lib/src/commands/assertNumElements.js +17 -0
  56. package/lib/src/commands/assertNumElements.js.map +1 -0
  57. package/lib/src/commands/extractDataFromClass.d.ts +18 -0
  58. package/lib/src/commands/extractDataFromClass.js +25 -0
  59. package/lib/src/commands/extractDataFromClass.js.map +1 -0
  60. package/lib/src/commands/extractDataFromClassByContents.d.ts +19 -0
  61. package/lib/src/commands/extractDataFromClassByContents.js +26 -0
  62. package/lib/src/commands/extractDataFromClassByContents.js.map +1 -0
  63. package/lib/src/commands/getJSON.d.ts +16 -0
  64. package/lib/src/commands/getJSON.js +21 -0
  65. package/lib/src/commands/getJSON.js.map +1 -0
  66. package/lib/src/commands/getNumElements.d.ts +15 -0
  67. package/lib/src/commands/getNumElements.js +19 -0
  68. package/lib/src/commands/getNumElements.js.map +1 -0
  69. package/lib/src/commands/handleHarvardKey.d.ts +15 -0
  70. package/lib/src/commands/handleHarvardKey.js +53 -0
  71. package/lib/src/commands/handleHarvardKey.js.map +1 -0
  72. package/lib/src/commands/launchAs.d.ts +25 -0
  73. package/lib/src/commands/launchAs.js +66 -0
  74. package/lib/src/commands/launchAs.js.map +1 -0
  75. package/lib/src/commands/launchLTIUsingToken.d.ts +21 -0
  76. package/lib/src/commands/launchLTIUsingToken.js +68 -0
  77. package/lib/src/commands/launchLTIUsingToken.js.map +1 -0
  78. package/lib/src/commands/navigateToHref.d.ts +15 -0
  79. package/lib/src/commands/navigateToHref.js +35 -0
  80. package/lib/src/commands/navigateToHref.js.map +1 -0
  81. package/lib/src/commands/runScript.d.ts +15 -0
  82. package/lib/src/commands/runScript.js +20 -0
  83. package/lib/src/commands/runScript.js.map +1 -0
  84. package/lib/src/commands/typeInto.d.ts +24 -0
  85. package/lib/src/commands/typeInto.js +41 -0
  86. package/lib/src/commands/typeInto.js.map +1 -0
  87. package/lib/src/commands/visitCanvasGETEndpoint.d.ts +20 -0
  88. package/lib/src/commands/visitCanvasGETEndpoint.js +26 -0
  89. package/lib/src/commands/visitCanvasGETEndpoint.js.map +1 -0
  90. package/lib/src/commands/waitForElementVisible.d.ts +16 -0
  91. package/lib/src/commands/waitForElementVisible.js +19 -0
  92. package/lib/src/commands/waitForElementVisible.js.map +1 -0
  93. package/lib/src/genConfiguration/helpers/resolveDependents.d.ts +13 -0
  94. package/lib/src/genConfiguration/helpers/resolveDependents.js +50 -0
  95. package/lib/src/genConfiguration/helpers/resolveDependents.js.map +1 -0
  96. package/lib/src/genConfiguration/helpers/splitEnv.d.ts +10 -0
  97. package/lib/src/genConfiguration/helpers/splitEnv.js +27 -0
  98. package/lib/src/genConfiguration/helpers/splitEnv.js.map +1 -0
  99. package/{src/genConfiguration.ts → lib/src/genConfiguration/index.d.ts} +3 -6
  100. package/lib/src/genConfiguration/index.js +88 -0
  101. package/lib/src/genConfiguration/index.js.map +1 -0
  102. package/lib/src/genConfiguration/types/DependentValue.d.ts +11 -0
  103. package/lib/src/genConfiguration/types/DependentValue.js +3 -0
  104. package/lib/src/genConfiguration/types/DependentValue.js.map +1 -0
  105. package/lib/src/genConfiguration/types/GlobalsOrProfile.d.ts +9 -0
  106. package/lib/src/genConfiguration/types/GlobalsOrProfile.js +3 -0
  107. package/lib/src/genConfiguration/types/GlobalsOrProfile.js.map +1 -0
  108. package/lib/src/genConfiguration/types/SplitEnv.d.ts +13 -0
  109. package/lib/src/genConfiguration/types/SplitEnv.js +3 -0
  110. package/lib/src/genConfiguration/types/SplitEnv.js.map +1 -0
  111. package/lib/{index.d.ts → src/index.d.ts} +1 -1
  112. package/lib/{index.js → src/index.js} +6 -3
  113. package/lib/src/index.js.map +1 -0
  114. package/lib/src/init.d.ts +6 -0
  115. package/lib/src/init.js +45 -0
  116. package/lib/src/init.js.map +1 -0
  117. package/lib/start/constants/AVAILABLE_BROWSERS.d.ts +9 -0
  118. package/lib/start/constants/AVAILABLE_BROWSERS.js +27 -0
  119. package/lib/start/constants/AVAILABLE_BROWSERS.js.map +1 -0
  120. package/lib/start/helpers/exec.d.ts +8 -0
  121. package/lib/start/helpers/exec.js +18 -0
  122. package/lib/start/helpers/exec.js.map +1 -0
  123. package/lib/start/helpers/extractArgValue.d.ts +10 -0
  124. package/lib/start/helpers/extractArgValue.js +39 -0
  125. package/lib/start/helpers/extractArgValue.js.map +1 -0
  126. package/lib/start/helpers/findProfilesByNames.d.ts +16 -0
  127. package/lib/start/helpers/findProfilesByNames.js +35 -0
  128. package/lib/start/helpers/findProfilesByNames.js.map +1 -0
  129. package/lib/start/helpers/parseCommaSeparated.d.ts +8 -0
  130. package/lib/start/helpers/parseCommaSeparated.js +23 -0
  131. package/lib/start/helpers/parseCommaSeparated.js.map +1 -0
  132. package/lib/start/helpers/print.d.ts +38 -0
  133. package/lib/start/helpers/print.js +145 -0
  134. package/lib/start/helpers/print.js.map +1 -0
  135. package/lib/start/helpers/prompt.d.ts +8 -0
  136. package/lib/start/helpers/prompt.js +25 -0
  137. package/lib/start/helpers/prompt.js.map +1 -0
  138. package/lib/start/helpers/showChooser.d.ts +21 -0
  139. package/lib/start/helpers/showChooser.js +116 -0
  140. package/lib/start/helpers/showChooser.js.map +1 -0
  141. package/lib/start/helpers/validateBrowsers.d.ts +8 -0
  142. package/lib/start/helpers/validateBrowsers.js +36 -0
  143. package/lib/start/helpers/validateBrowsers.js.map +1 -0
  144. package/lib/start/index.d.ts +7 -0
  145. package/lib/start/index.js +139 -0
  146. package/lib/start/index.js.map +1 -0
  147. package/lib/start/types/ChooserOption.d.ts +10 -0
  148. package/lib/start/types/ChooserOption.js +3 -0
  149. package/lib/start/types/ChooserOption.js.map +1 -0
  150. package/package.json +28 -8
  151. package/setup/addToGitIgnore.ts +47 -0
  152. package/setup/checkRequiredFiles.ts +62 -0
  153. package/setup/genCommandImportFile.ts +75 -0
  154. package/setup/genDynamicConfigFile.ts +28 -0
  155. package/setup/index.ts +11 -0
  156. package/setup/setupCypressDependencies.ts +38 -0
  157. package/src/commands/assertDoesNotHaveClass.ts +51 -0
  158. package/src/commands/assertHasClass.ts +51 -0
  159. package/src/commands/assertNumElements.ts +50 -0
  160. package/src/commands/extractDataFromClass.ts +52 -0
  161. package/src/commands/extractDataFromClassByContents.ts +55 -0
  162. package/src/commands/getJSON.ts +45 -0
  163. package/src/commands/getNumElements.ts +45 -0
  164. package/src/commands/handleHarvardKey.ts +91 -0
  165. package/src/commands/launchAs.ts +120 -0
  166. package/src/commands/launchLTIUsingToken.ts +115 -0
  167. package/src/commands/navigateToHref.ts +60 -0
  168. package/src/commands/runScript.ts +44 -0
  169. package/src/commands/typeInto.ts +88 -0
  170. package/src/commands/visitCanvasGETEndpoint.ts +61 -0
  171. package/src/commands/waitForElementVisible.ts +49 -0
  172. package/src/genConfiguration/helpers/resolveDependents.ts +47 -0
  173. package/src/genConfiguration/helpers/splitEnv.ts +30 -0
  174. package/src/genConfiguration/index.ts +94 -0
  175. package/src/genConfiguration/types/DependentValue.ts +14 -0
  176. package/src/genConfiguration/types/GlobalsOrProfile.ts +12 -0
  177. package/src/genConfiguration/types/SplitEnv.ts +18 -0
  178. package/src/index.ts +7 -4
  179. package/src/init.ts +36 -17
  180. package/start/constants/AVAILABLE_BROWSERS.ts +28 -0
  181. package/start/helpers/exec.ts +17 -0
  182. package/start/helpers/extractArgValue.ts +42 -0
  183. package/start/helpers/findProfilesByNames.ts +39 -0
  184. package/start/helpers/parseCommaSeparated.ts +23 -0
  185. package/start/helpers/print.ts +155 -0
  186. package/start/helpers/prompt.ts +23 -0
  187. package/start/helpers/showChooser.ts +140 -0
  188. package/start/helpers/validateBrowsers.ts +35 -0
  189. package/start/index.ts +163 -0
  190. package/start/types/ChooserOption.ts +11 -0
  191. package/tsconfig.json +3 -4
  192. package/.eslintrc.json +0 -58
  193. package/lib/genConfiguration.d.ts +0 -8
  194. package/lib/genConfiguration.js +0 -13
  195. package/lib/genConfiguration.js.map +0 -1
  196. package/lib/index.js.map +0 -1
  197. package/lib/init.d.ts +0 -13
  198. package/lib/init.js +0 -10
  199. package/lib/init.js.map +0 -1
@@ -0,0 +1,30 @@
1
+ // Import shared types
2
+ import type GlobalsOrProfile from '../types/GlobalsOrProfile';
3
+ import type DependentValue from '../types/DependentValue';
4
+ import type SplitEnv from '../types/SplitEnv';
5
+
6
+ /**
7
+ * Split one map into basic and dependent values
8
+ * @author Gardenia Liu
9
+ * @param map to split
10
+ * @returns split basic and dependent values map
11
+ */
12
+ const splitEnv = (map: GlobalsOrProfile): SplitEnv => {
13
+ const basic: GlobalsOrProfile = {};
14
+ const dependent: { [k: string]: DependentValue } = {};
15
+
16
+ Object.entries(map).forEach(([k, v]) => {
17
+ // If entry has dependsOn relationship, add it to dependent dictionary
18
+ if (typeof v === 'object' && 'dependsOn' in v) {
19
+ dependent[k] = v as DependentValue;
20
+ // Otherwise, add it to basic dictionary
21
+ } else {
22
+ basic[k] = v;
23
+ }
24
+ });
25
+
26
+ // Returns separated basic and dependent values
27
+ return { basic, dependent };
28
+ };
29
+
30
+ export default splitEnv;
@@ -0,0 +1,94 @@
1
+ /* eslint-disable no-console */
2
+ /* eslint-disable global-require */
3
+ /* eslint-disable import/no-dynamic-require */
4
+ import path from 'path';
5
+
6
+ // Import cypress
7
+ import { defineConfig } from 'cypress';
8
+
9
+ // Import external libraries
10
+ import deepmerge from 'deepmerge';
11
+
12
+ // Import helpers
13
+ import splitEnv from './helpers/splitEnv';
14
+ import resolveDependents from './helpers/resolveDependents';
15
+
16
+ // Import types
17
+ import type SplitEnv from './types/SplitEnv';
18
+ import type GlobalsOrProfile from './types/GlobalsOrProfile';
19
+
20
+ // Determine project directory
21
+ const CWD = path.join(__dirname, '../..');
22
+
23
+ /**
24
+ * Generate Cypress configuration based on global credentials,
25
+ * variables, resources, and profiles
26
+ * @author Gardenia Liu
27
+ * @author Gabe Abrams
28
+ */
29
+ const genConfiguration = () => {
30
+ // Define paths
31
+ const GLOBALS_PATH = path.join(CWD, '/cypress/globals');
32
+ const PROFILES_PATH = path.join(CWD, '/cypress/profiles');
33
+
34
+ // Import global env var objects
35
+ const GlobalCredentials: GlobalsOrProfile = require(path.join(GLOBALS_PATH, '/GlobalCredentials'));
36
+ const GlobalResources: GlobalsOrProfile = require(path.join(GLOBALS_PATH, '/GlobalResources'));
37
+ const GlobalValues: GlobalsOrProfile = require(path.join(GLOBALS_PATH, '/GlobalValues'));
38
+
39
+ // Determine profile file name (default is stage)
40
+ const profileName = process.env.CYPRESS_PROFILE || 'stage';
41
+
42
+ // Load in chosen profile configuration
43
+ let profileModules: GlobalsOrProfile = {};
44
+ try {
45
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
46
+ const mod = require(path.join(PROFILES_PATH, `/${profileName}.Profile`));
47
+ profileModules = (mod?.default ?? mod ?? {}) as GlobalsOrProfile;
48
+ } catch (e) {
49
+ console.warn(`[cypress.config] Could not load profile '${profileName}.Profile'`, e);
50
+ process.exit(1);
51
+ }
52
+
53
+ // NOTE: Order matters (later overrides earlier)
54
+ const sources: GlobalsOrProfile[] = [GlobalCredentials, GlobalResources, GlobalValues, profileModules];
55
+
56
+ // Split all sources into basic and dependent
57
+ const splits: SplitEnv[] = sources.map((source) => {
58
+ return splitEnv(source);
59
+ });
60
+
61
+ // Extract basic and dependent values from splits
62
+ const basicValues = splits.map((s) => {
63
+ return s.basic;
64
+ });
65
+ const dependentValues = splits.map((s) => {
66
+ return s.dependent;
67
+ });
68
+
69
+ // Merge all of the basics
70
+ const basics: GlobalsOrProfile = deepmerge.all<GlobalsOrProfile>(basicValues);
71
+
72
+ // Resolve dependencies
73
+ const dependents: GlobalsOrProfile = resolveDependents(dependentValues, basics);
74
+
75
+ // Add basics, depends, and profileName into the final env
76
+ const finalEnv: GlobalsOrProfile = {
77
+ ...basics,
78
+ ...dependents,
79
+ profileName,
80
+ };
81
+
82
+ const baseUrl = finalEnv.baseURL;
83
+
84
+ // Return the configuration object
85
+ return defineConfig({
86
+ e2e: {
87
+ experimentalOriginDependencies: true,
88
+ baseUrl,
89
+ env: finalEnv,
90
+ },
91
+ });
92
+ };
93
+
94
+ export default genConfiguration;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Value for a dependent entry
3
+ * @author Gardenia Liu
4
+ */
5
+ type DependentValue = {
6
+ dependsOn: string,
7
+ } & {
8
+ // Environment, global variables, profile values, etc.
9
+ [k: string]: any,
10
+ // Default value if the dependsOn value is not found
11
+ default?: any,
12
+ };
13
+
14
+ export default DependentValue;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Object containing global variables, profile values, etc.
3
+ * @author Gardenia Liu
4
+ */
5
+ type GlobalsOrProfile = {
6
+ // Base URL to prefix all paths with
7
+ baseURL?: string,
8
+ // Environment, global variables, profile values, etc.
9
+ [k: string]: any,
10
+ };
11
+
12
+ export default GlobalsOrProfile;
@@ -0,0 +1,18 @@
1
+ // Import shared types
2
+ import type GlobalsOrProfile from './GlobalsOrProfile';
3
+ import type DependentValue from './DependentValue';
4
+
5
+ /**
6
+ * Env type where basic and dependent entries are separated
7
+ * @author Gardenia Liu
8
+ */
9
+ type SplitEnv = {
10
+ // Basic entries
11
+ basic: GlobalsOrProfile,
12
+ // Dependent entries
13
+ dependent: {
14
+ [k: string]: DependentValue,
15
+ },
16
+ };
17
+
18
+ export default SplitEnv;
package/src/index.ts CHANGED
@@ -1,11 +1,14 @@
1
+ // Import helpers
1
2
  import init from './init';
2
3
  import genConfiguration from './genConfiguration';
3
4
 
4
- // Automatically initialize upon importing the library
5
- init();
5
+ // Automatically initialize upon importing the library, only run in Cypress context
6
+ if (typeof Cypress !== 'undefined') {
7
+ init();
8
+ }
6
9
 
7
- // Exports
8
10
  export {
9
- genConfiguration,
11
+ // Helpers
10
12
  init,
13
+ genConfiguration,
11
14
  };
package/src/init.ts CHANGED
@@ -1,22 +1,41 @@
1
- /// <reference types="cypress" />
2
-
3
- // Add type declarations to Cypress namespace
4
- declare global {
5
- namespace Cypress {
6
- interface Chainable {
7
- /**
8
- * Custom command to click an element by selector
9
- * @example cy.clickSomething('.my-button')
10
- */
11
- clickSomething(selector: string): Chainable<Element>;
12
- }
13
- }
14
- }
1
+ // Import all commands
2
+ import assertDoesNotHaveClass from './commands/assertDoesNotHaveClass';
3
+ import assertHasClass from './commands/assertHasClass';
4
+ import assertNumElements from './commands/assertNumElements';
5
+ import extractDataFromClass from './commands/extractDataFromClass';
6
+ import extractDataFromClassByContents from './commands/extractDataFromClassByContents';
7
+ import getJSON from './commands/getJSON';
8
+ import getNumElements from './commands/getNumElements';
9
+ import handleHarvardKey from './commands/handleHarvardKey';
10
+ import launchAs from './commands/launchAs';
11
+ import launchLTIUsingToken from './commands/launchLTIUsingToken';
12
+ import navigateToHref from './commands/navigateToHref';
13
+ import runScript from './commands/runScript';
14
+ import typeInto from './commands/typeInto';
15
+ import visitCanvasGETEndpoint from './commands/visitCanvasGETEndpoint';
16
+ import waitForElementVisible from './commands/waitForElementVisible';
15
17
 
18
+ /**
19
+ * Initialize custom commands
20
+ * @author Gabe Abrams
21
+ */
16
22
  const init = () => {
17
- Cypress.Commands.add('clickSomething', (selector: string) => {
18
- cy.get(selector).click();
19
- });
23
+ // Execute each command adder
24
+ assertDoesNotHaveClass();
25
+ assertHasClass();
26
+ assertNumElements();
27
+ getNumElements();
28
+ handleHarvardKey();
29
+ launchAs();
30
+ launchLTIUsingToken();
31
+ navigateToHref();
32
+ runScript();
33
+ typeInto();
34
+ visitCanvasGETEndpoint();
35
+ waitForElementVisible();
36
+ extractDataFromClass();
37
+ extractDataFromClassByContents();
38
+ getJSON();
20
39
  };
21
40
 
22
41
  export default init;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Available browsers for Cypress tests
3
+ * @author Yuen Ler Chow
4
+ */
5
+ const AVAILABLE_BROWSERS: {
6
+ name: string,
7
+ tag: string,
8
+ }[] = [
9
+ {
10
+ name: 'chrome',
11
+ tag: 'C',
12
+ },
13
+ {
14
+ name: 'firefox',
15
+ tag: 'F',
16
+ },
17
+ {
18
+ name: 'edge',
19
+ tag: 'E',
20
+ },
21
+ {
22
+ name: 'safari',
23
+ tag: 'S',
24
+ },
25
+ ];
26
+
27
+ // Export only the browser names in lowercase
28
+ export default AVAILABLE_BROWSERS;
@@ -0,0 +1,17 @@
1
+ import { execSync } from 'child_process';
2
+
3
+ /**
4
+ * Execute a command
5
+ * @author Gabe Abrams
6
+ * @param command the command to execute
7
+ * @param printToStdOut if true, print to standard out instead of returning
8
+ */
9
+ const exec = (command: string, printToStdOut?: boolean): string => {
10
+ if (printToStdOut) {
11
+ execSync(command, { stdio: 'inherit' });
12
+ return '';
13
+ }
14
+ return execSync(command).toString();
15
+ };
16
+
17
+ export default exec;
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Extract CLI argument value or check if flag exists
3
+ * @author Gardenia Liu
4
+ * @author Yuen Ler Chow
5
+ * @param args array of CLI arguments
6
+ * @param argName the argument name to look for (e.g., '--profile' or '--headless')
7
+ * @returns the argument value if found, empty string if flag exists without value, undefined otherwise
8
+ */
9
+ const extractArgValue = (args: string[], argName: string): string | undefined => {
10
+ let matchedValue: string | undefined;
11
+ args.some((rawArg, index) => {
12
+ const normalizedArg = rawArg.toLowerCase();
13
+
14
+ // Case 1: flag and value combined, e.g. "--profile=stage"
15
+ if (normalizedArg.startsWith(`${argName}=`)) {
16
+ const [, value] = rawArg.split('=');
17
+ matchedValue = value;
18
+ return true; // stop scanning
19
+ }
20
+
21
+ // Case 2: flag by itself, possibly followed by a separate value
22
+ if (normalizedArg === argName) {
23
+ const nextArg = args[index + 1];
24
+
25
+ // If the next token exists and isn't another flag, treat it as the value
26
+ if (nextArg && !nextArg.startsWith('--')) {
27
+ matchedValue = nextArg;
28
+ } else {
29
+ // Flag exists but no value given (boolean-style flag) (e.g. "--headless")
30
+ matchedValue = '';
31
+ }
32
+
33
+ return true; // stop scanning
34
+ }
35
+
36
+ return false;
37
+ });
38
+
39
+ return matchedValue;
40
+ };
41
+
42
+ export default extractArgValue;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Find profiles by names
3
+ * @author Gardenia Liu
4
+ * @author Yuen Ler Chow
5
+ * @param names array of profile names to find
6
+ * @param profiles array of available profiles
7
+ * @returns array of found profiles
8
+ */
9
+ const findProfilesByNames = (
10
+ names: string[],
11
+ profiles: Array<{ file: string; profileName: string }>,
12
+ ): Array<{ file: string; profileName: string }> => {
13
+ const found: Array<{ file: string; profileName: string }> = [];
14
+ const notFound: string[] = [];
15
+
16
+ names.forEach((name) => {
17
+ const target = name.toLowerCase();
18
+
19
+ // Find matching profile (case-insensitive)
20
+ const match = profiles.find((p) => {
21
+ return p.profileName.toLowerCase() === target;
22
+ });
23
+ if (match) {
24
+ found.push(match);
25
+ } else {
26
+ notFound.push(name);
27
+ }
28
+ });
29
+
30
+ if (notFound.length > 0) {
31
+ // eslint-disable-next-line no-console
32
+ console.error(`Profile(s) not found: ${notFound.join(', ')}. Available profiles: ${profiles.map((p) => { return p.profileName; }).join(', ')}`);
33
+ process.exit(1);
34
+ }
35
+
36
+ return found;
37
+ };
38
+
39
+ export default findProfilesByNames;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Parse comma-separated values into an array
3
+ * @author Yuen Ler Chow
4
+ * @param commaSeparatedString the comma-separated string to parse
5
+ * @returns array of trimmed, non-empty values
6
+ */
7
+ const parseCommaSeparated = (commaSeparatedString?: string): string[] => {
8
+ if (!commaSeparatedString) {
9
+ return [];
10
+ }
11
+ return (
12
+ commaSeparatedString
13
+ .split(',')
14
+ .map((value) => {
15
+ return value.trim();
16
+ })
17
+ .filter((value) => {
18
+ return value.length > 0;
19
+ })
20
+ );
21
+ };
22
+
23
+ export default parseCommaSeparated;
@@ -0,0 +1,155 @@
1
+ /* eslint-disable no-console */
2
+
3
+ /**
4
+ * Get the left buffer for a centered message
5
+ * @author Gabe Abrams
6
+ * @param message message to print
7
+ * @param padding amount of padding to add
8
+ * @returns number of chars in the buffer
9
+ */
10
+ const leftBuffer = (message: string, padding: number): number => {
11
+ return (Math.floor(process.stdout.columns / 2) - padding - Math.ceil(message.length / 2));
12
+ };
13
+
14
+ /**
15
+ * Get the right buffer for a centered message
16
+ * @author Gabe Abrams
17
+ * @param message message to print
18
+ * @param padding amount of padding to add
19
+ * @returns number of chars in the buffer
20
+ */
21
+ const rightBuffer = (message: string, padding: number): number => {
22
+ return (Math.ceil(process.stdout.columns / 2) - padding - Math.floor(message.length / 2));
23
+ };
24
+
25
+ /**
26
+ * Surround text with a border and spaces
27
+ * @author Gabe Abrams
28
+ * @param str text to print
29
+ * @param border single character to use as a border
30
+ * @returns text to print
31
+ */
32
+ const surroundWithBuffer = (str: string, border: string): string => {
33
+ return (
34
+ border
35
+ + ' '.repeat(leftBuffer(str, border.length))
36
+ + str
37
+ + ' '.repeat(rightBuffer(str, border.length))
38
+ + border
39
+ );
40
+ };
41
+
42
+ /**
43
+ * Surround text with a character as the buffer
44
+ * @author Gabe Abrams
45
+ * @param str text to print
46
+ * @param char character to place as the buffer
47
+ * @returns text to print
48
+ */
49
+ const surroundWithChars = (str: string, char: string): string => {
50
+ if (str.length > process.stdout.columns) {
51
+ return str;
52
+ }
53
+ if (str.length === process.stdout.columns - 1) {
54
+ return char + str;
55
+ }
56
+ if (str.length === process.stdout.columns - 2) {
57
+ return char + str + char;
58
+ }
59
+ return (
60
+ char.repeat(leftBuffer(str, 1))
61
+ + ' '
62
+ + str
63
+ + ' '
64
+ + char.repeat(rightBuffer(str, 1))
65
+ );
66
+ };
67
+
68
+ // Prompt instance
69
+ let cachedPrompt: any;
70
+
71
+ const print = {
72
+ /**
73
+ * Print a title
74
+ * @author Gabe Abrams
75
+ * @param str text to print
76
+ */
77
+ title: (str: string) => {
78
+ if (str.length > process.stdout.columns) {
79
+ return console.log(str);
80
+ }
81
+ console.log('\u2554' + '\u2550'.repeat(process.stdout.columns - 2) + '\u2557');
82
+ console.log(surroundWithBuffer(str, '\u2551'));
83
+ console.log('\u255A' + '\u2550'.repeat(process.stdout.columns - 2) + '\u255D');
84
+ },
85
+ /**
86
+ * Print a sub title (subheading)
87
+ * @author Gabe Abrams
88
+ * @param str text to print
89
+ */
90
+ subtitle: (str: string) => {
91
+ if (str.length > process.stdout.columns) {
92
+ return console.log(str);
93
+ }
94
+ console.log(surroundWithChars(str, '\u257C'));
95
+ },
96
+ /**
97
+ * Print centered text
98
+ * @author Gabe Abrams
99
+ * @param str text to print
100
+ */
101
+ centered: (str: string) => {
102
+ const lines = [];
103
+ let index = 0;
104
+ while (index < str.length) {
105
+ lines.push(str.substring(index, Math.min(index + process.stdout.columns, str.length)));
106
+ index += process.stdout.columns;
107
+ }
108
+ lines.forEach((line, lineIndex) => {
109
+ if (lineIndex !== lines.length - 1) {
110
+ // No need to center: fills whole line
111
+ console.log(line);
112
+ } else {
113
+ // This line needs to be centered
114
+ console.log(surroundWithChars(line, ' '));
115
+ }
116
+ });
117
+ },
118
+ /**
119
+ * Print a fatal error message
120
+ * @author Gabe Abrams
121
+ * @param err error message
122
+ */
123
+ fatalError: (err: string) => {
124
+ console.log('\n');
125
+ const errLine1 = err.substring(0, process.stdout.columns - 6);
126
+ const errLine2 = err.substring(process.stdout.columns - 6);
127
+ console.log('\u2554' + '\u2550'.repeat(3) + '\u2557 ');
128
+ console.log(`\u2551 ! \u2551 ${errLine1}`);
129
+ console.log('\u255A' + '\u2550'.repeat(3) + '\u255D ' + errLine2);
130
+ process.exit(0);
131
+ },
132
+ /**
133
+ * Save a copy of the prompt instance
134
+ * @author Gabe Abrams
135
+ * @param promptInstance instance of prompt-sync
136
+ */
137
+ savePrompt: (promptInstance: any) => {
138
+ cachedPrompt = promptInstance;
139
+ },
140
+ /**
141
+ * Ask the user to press enter before continuing
142
+ * @author Gabe Abrams
143
+ */
144
+ enterToContinue: () => {
145
+ const res = cachedPrompt(
146
+ surroundWithChars('enter to continue, ctrl+c to quit', '\u257C'),
147
+ true,
148
+ );
149
+ if (res === null) {
150
+ process.exit(0);
151
+ }
152
+ },
153
+ };
154
+
155
+ export default print;
@@ -0,0 +1,23 @@
1
+ import initPrompt from 'prompt-sync';
2
+ import print from './print';
3
+
4
+ const promptSync = initPrompt();
5
+
6
+ /**
7
+ * Ask the user a question
8
+ * @param title title of the question
9
+ * @param notRequired true if question is not required
10
+ * @returns response
11
+ */
12
+ const prompt = (title: string, notRequired?: boolean): string => {
13
+ const val = promptSync(title);
14
+ if (val === null || (!notRequired && !val)) {
15
+ process.exit(0);
16
+ }
17
+ return val;
18
+ };
19
+
20
+ // Save the prompt for use later
21
+ print.savePrompt(prompt);
22
+
23
+ export default prompt;