@ronaldphilipsen/local-action 0.0.0-stage → 7.1.0

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 (223) hide show
  1. package/.checkov.yml +3 -0
  2. package/.env.example +379 -0
  3. package/.github/codeql/codeql-config.yml +7 -0
  4. package/.github/copilot-instructions.md +79 -0
  5. package/.github/dependabot.yml +32 -0
  6. package/.github/workflows/codeql-analysis.yml +53 -0
  7. package/.github/workflows/continuous-delivery.yml +93 -0
  8. package/.github/workflows/continuous-integration.yml +382 -0
  9. package/.github/workflows/linter.yml +47 -0
  10. package/.github/workflows/release.yaml +99 -0
  11. package/.github/workflows/version-check.yml +44 -0
  12. package/.grype.yml +151 -0
  13. package/.markdown-lint.yml +7 -0
  14. package/.mega-linter.yml +188 -0
  15. package/.node-version +1 -0
  16. package/.prettierignore +4 -0
  17. package/.prettierrc.yml +14 -0
  18. package/.vscode/launch.json +15 -0
  19. package/.vscode/mcp.json +9 -0
  20. package/.vscode/settings.json +15 -0
  21. package/.vscode/tasks.json +51 -0
  22. package/.yaml-lint.yml +10 -0
  23. package/CHANGELOG.md +75 -0
  24. package/CODEOWNERS +3 -0
  25. package/CODE_OF_CONDUCT.md +74 -0
  26. package/CONTRIBUTING.md +140 -0
  27. package/LICENSE +21 -0
  28. package/README.md +285 -2
  29. package/SECURITY.md +42 -0
  30. package/SUPPORT.md +19 -0
  31. package/__fixtures__/@actions/core.ts +14 -0
  32. package/__fixtures__/@actions/exec.ts +9 -0
  33. package/__fixtures__/@actions/github.ts +3 -0
  34. package/__fixtures__/@actions/http-client.ts +9 -0
  35. package/__fixtures__/@actions/io.ts +9 -0
  36. package/__fixtures__/@octokit/rest.ts +18 -0
  37. package/__fixtures__/crypto.ts +7 -0
  38. package/__fixtures__/fs.ts +27 -0
  39. package/__fixtures__/javascript/no-export/.env.fixture +2 -0
  40. package/__fixtures__/javascript/no-export/action.yml +16 -0
  41. package/__fixtures__/javascript/no-export/node_modules/.gitkeep +0 -0
  42. package/__fixtures__/javascript/no-export/package.json +12 -0
  43. package/__fixtures__/javascript/no-export/src/index.js +0 -0
  44. package/__fixtures__/javascript/no-export/src/main.js +0 -0
  45. package/__fixtures__/javascript/no-import/.env.fixture +2 -0
  46. package/__fixtures__/javascript/no-import/action.yml +16 -0
  47. package/__fixtures__/javascript/no-import/node_modules/.gitkeep +0 -0
  48. package/__fixtures__/javascript/no-import/package.json +12 -0
  49. package/__fixtures__/javascript/no-import/src/index.js +3 -0
  50. package/__fixtures__/javascript/no-import/src/main.js +7 -0
  51. package/__fixtures__/javascript/success/.env.fixture +2 -0
  52. package/__fixtures__/javascript/success/action.yml +16 -0
  53. package/__fixtures__/javascript/success/node_modules/.gitkeep +0 -0
  54. package/__fixtures__/javascript/success/package.json +12 -0
  55. package/__fixtures__/javascript/success/post/index.js +3 -0
  56. package/__fixtures__/javascript/success/post/main.js +13 -0
  57. package/__fixtures__/javascript/success/pre/index.js +3 -0
  58. package/__fixtures__/javascript/success/pre/main.js +13 -0
  59. package/__fixtures__/javascript/success/src/index.js +3 -0
  60. package/__fixtures__/javascript/success/src/main.js +13 -0
  61. package/__fixtures__/javascript-esm/no-import/.env.fixture +2 -0
  62. package/__fixtures__/javascript-esm/no-import/action.yml +16 -0
  63. package/__fixtures__/javascript-esm/no-import/node_modules/.gitkeep +0 -0
  64. package/__fixtures__/javascript-esm/no-import/package.json +13 -0
  65. package/__fixtures__/javascript-esm/no-import/src/index.js +3 -0
  66. package/__fixtures__/javascript-esm/no-import/src/main.js +7 -0
  67. package/__fixtures__/javascript-esm/success/.env.fixture +2 -0
  68. package/__fixtures__/javascript-esm/success/action.yml +16 -0
  69. package/__fixtures__/javascript-esm/success/node_modules/.gitkeep +0 -0
  70. package/__fixtures__/javascript-esm/success/package.json +13 -0
  71. package/__fixtures__/javascript-esm/success/post/index.js +3 -0
  72. package/__fixtures__/javascript-esm/success/post/main.js +13 -0
  73. package/__fixtures__/javascript-esm/success/pre/index.js +3 -0
  74. package/__fixtures__/javascript-esm/success/pre/main.js +13 -0
  75. package/__fixtures__/javascript-esm/success/src/index.js +3 -0
  76. package/__fixtures__/javascript-esm/success/src/main.js +13 -0
  77. package/__fixtures__/path.ts +7 -0
  78. package/__fixtures__/payloads/issue_opened.json +33 -0
  79. package/__fixtures__/stream/promises.ts +3 -0
  80. package/__fixtures__/tsconfig-paths.ts +4 -0
  81. package/__fixtures__/typescript/no-import/.env.fixture +2 -0
  82. package/__fixtures__/typescript/no-import/action.yml +16 -0
  83. package/__fixtures__/typescript/no-import/node_modules/.gitkeep +0 -0
  84. package/__fixtures__/typescript/no-import/package.json +15 -0
  85. package/__fixtures__/typescript/no-import/src/index.ts +3 -0
  86. package/__fixtures__/typescript/no-import/src/main.ts +3 -0
  87. package/__fixtures__/typescript/no-import/tsconfig.json +20 -0
  88. package/__fixtures__/typescript/success/.env.fixture +2 -0
  89. package/__fixtures__/typescript/success/action.yml +16 -0
  90. package/__fixtures__/typescript/success/node_modules/.gitkeep +0 -0
  91. package/__fixtures__/typescript/success/package.json +15 -0
  92. package/__fixtures__/typescript/success/post/index.ts +3 -0
  93. package/__fixtures__/typescript/success/post/main.ts +9 -0
  94. package/__fixtures__/typescript/success/pre/index.ts +3 -0
  95. package/__fixtures__/typescript/success/pre/main.ts +9 -0
  96. package/__fixtures__/typescript/success/src/index.ts +3 -0
  97. package/__fixtures__/typescript/success/src/main.ts +9 -0
  98. package/__fixtures__/typescript/success/tsconfig.json +20 -0
  99. package/__fixtures__/typescript/success-yaml/.env.fixture +2 -0
  100. package/__fixtures__/typescript/success-yaml/action.yaml +16 -0
  101. package/__fixtures__/typescript/success-yaml/node_modules/.gitkeep +0 -0
  102. package/__fixtures__/typescript/success-yaml/package.json +15 -0
  103. package/__fixtures__/typescript/success-yaml/post/index.ts +3 -0
  104. package/__fixtures__/typescript/success-yaml/post/main.ts +9 -0
  105. package/__fixtures__/typescript/success-yaml/pre/index.ts +3 -0
  106. package/__fixtures__/typescript/success-yaml/pre/main.ts +9 -0
  107. package/__fixtures__/typescript/success-yaml/src/index.ts +3 -0
  108. package/__fixtures__/typescript/success-yaml/src/main.ts +9 -0
  109. package/__fixtures__/typescript/success-yaml/tsconfig.json +20 -0
  110. package/__fixtures__/typescript-esm/no-export/.env.fixture +2 -0
  111. package/__fixtures__/typescript-esm/no-export/action.yml +17 -0
  112. package/__fixtures__/typescript-esm/no-export/node_modules/.gitkeep +0 -0
  113. package/__fixtures__/typescript-esm/no-export/package.json +17 -0
  114. package/__fixtures__/typescript-esm/no-export/src/index.ts +0 -0
  115. package/__fixtures__/typescript-esm/no-export/src/main.ts +0 -0
  116. package/__fixtures__/typescript-esm/no-export/tsconfig.json +25 -0
  117. package/__fixtures__/typescript-esm/no-import/.env.fixture +2 -0
  118. package/__fixtures__/typescript-esm/no-import/action.yml +17 -0
  119. package/__fixtures__/typescript-esm/no-import/node_modules/.gitkeep +0 -0
  120. package/__fixtures__/typescript-esm/no-import/package.json +17 -0
  121. package/__fixtures__/typescript-esm/no-import/src/index.ts +3 -0
  122. package/__fixtures__/typescript-esm/no-import/src/main.ts +3 -0
  123. package/__fixtures__/typescript-esm/no-import/tsconfig.json +25 -0
  124. package/__fixtures__/typescript-esm/success/.env.fixture +2 -0
  125. package/__fixtures__/typescript-esm/success/action.yml +17 -0
  126. package/__fixtures__/typescript-esm/success/node_modules/.gitkeep +0 -0
  127. package/__fixtures__/typescript-esm/success/package.json +17 -0
  128. package/__fixtures__/typescript-esm/success/post/index.ts +3 -0
  129. package/__fixtures__/typescript-esm/success/post/main.ts +9 -0
  130. package/__fixtures__/typescript-esm/success/pre/index.ts +3 -0
  131. package/__fixtures__/typescript-esm/success/pre/main.ts +9 -0
  132. package/__fixtures__/typescript-esm/success/src/index.ts +3 -0
  133. package/__fixtures__/typescript-esm/success/src/main.ts +9 -0
  134. package/__fixtures__/typescript-esm/success/tsconfig.json +25 -0
  135. package/__fixtures__/typescript-esm/tsconfig-comments/.env.fixture +2 -0
  136. package/__fixtures__/typescript-esm/tsconfig-comments/action.yml +17 -0
  137. package/__fixtures__/typescript-esm/tsconfig-comments/node_modules/.gitkeep +0 -0
  138. package/__fixtures__/typescript-esm/tsconfig-comments/package.json +17 -0
  139. package/__fixtures__/typescript-esm/tsconfig-comments/src/index.ts +3 -0
  140. package/__fixtures__/typescript-esm/tsconfig-comments/src/main.ts +9 -0
  141. package/__fixtures__/typescript-esm/tsconfig-comments/tsconfig.json +21 -0
  142. package/__tests__/command.test.ts +224 -0
  143. package/__tests__/stubs/artifact/internal/client.test.ts +340 -0
  144. package/__tests__/stubs/artifact/internal/delete/delete-artifact.test.ts +152 -0
  145. package/__tests__/stubs/artifact/internal/download/download-artifact.test.ts +119 -0
  146. package/__tests__/stubs/artifact/internal/find/get-artifact.test.ts +237 -0
  147. package/__tests__/stubs/artifact/internal/find/list-artifacts.test.ts +218 -0
  148. package/__tests__/stubs/artifact/internal/find/retry-options.test.ts +54 -0
  149. package/__tests__/stubs/artifact/internal/shared/config.test.ts +91 -0
  150. package/__tests__/stubs/artifact/internal/shared/errors.test.ts +38 -0
  151. package/__tests__/stubs/artifact/internal/shared/user-agent.test.ts +22 -0
  152. package/__tests__/stubs/artifact/internal/upload/path-and-artifact-name-validation.test.ts +53 -0
  153. package/__tests__/stubs/artifact/internal/upload/upload-artifact.test.ts +126 -0
  154. package/__tests__/stubs/artifact/internal/upload/upload-zip-specification.test.ts +49 -0
  155. package/__tests__/stubs/cache/cache.test.ts +357 -0
  156. package/__tests__/stubs/cache/errors.test.ts +55 -0
  157. package/__tests__/stubs/cache/internal/cacheUtils.test.ts +109 -0
  158. package/__tests__/stubs/cache/internal/constants.test.ts +81 -0
  159. package/__tests__/stubs/cache/internal/tar.test.ts +129 -0
  160. package/__tests__/stubs/core/core.test.ts +760 -0
  161. package/__tests__/stubs/core/path-utils.test.ts +45 -0
  162. package/__tests__/stubs/core/platform.test.ts +89 -0
  163. package/__tests__/stubs/core/summary.test.ts +455 -0
  164. package/__tests__/stubs/env.test.ts +72 -0
  165. package/__tests__/stubs/github/context.test.ts +73 -0
  166. package/__tests__/stubs/github/github.test.ts +15 -0
  167. package/__tests__/stubs/github/internal/utils.test.ts +98 -0
  168. package/__tests__/stubs/github/utils.test.ts +16 -0
  169. package/__tests__/utils/config.test.ts +78 -0
  170. package/__tests__/utils/output.test.ts +32 -0
  171. package/__tests__/utils/package.test.ts +33 -0
  172. package/badges/coverage.svg +1 -0
  173. package/bin/local-action.js +121 -0
  174. package/docs/create-a-job-summary.md +32 -0
  175. package/docs/debugging-in-vscode.md +27 -0
  176. package/docs/img/job-summary.png +0 -0
  177. package/docs/supported-functionality.md +126 -0
  178. package/eslint.config.mjs +74 -0
  179. package/jest.config.ts +41 -0
  180. package/package.json +93 -4
  181. package/src/command.ts +171 -0
  182. package/src/commands/run.ts +616 -0
  183. package/src/index.ts +21 -0
  184. package/src/stubs/artifact/artifact.ts +17 -0
  185. package/src/stubs/artifact/internal/client.ts +400 -0
  186. package/src/stubs/artifact/internal/delete/delete-artifact.ts +126 -0
  187. package/src/stubs/artifact/internal/download/download-artifact.ts +309 -0
  188. package/src/stubs/artifact/internal/find/get-artifact.ts +135 -0
  189. package/src/stubs/artifact/internal/find/list-artifacts.ts +188 -0
  190. package/src/stubs/artifact/internal/find/retry-options.ts +57 -0
  191. package/src/stubs/artifact/internal/shared/config.ts +64 -0
  192. package/src/stubs/artifact/internal/shared/errors.ts +53 -0
  193. package/src/stubs/artifact/internal/shared/interfaces.ts +195 -0
  194. package/src/stubs/artifact/internal/shared/user-agent.ts +13 -0
  195. package/src/stubs/artifact/internal/upload/path-and-artifact-name-validation.ts +92 -0
  196. package/src/stubs/artifact/internal/upload/upload-artifact.ts +127 -0
  197. package/src/stubs/artifact/internal/upload/upload-zip-specification.ts +108 -0
  198. package/src/stubs/artifact/internal/upload/zip.ts +122 -0
  199. package/src/stubs/cache/cache.ts +334 -0
  200. package/src/stubs/cache/errors.ts +31 -0
  201. package/src/stubs/cache/interfaces.ts +4 -0
  202. package/src/stubs/cache/internal/cacheUtils.ts +207 -0
  203. package/src/stubs/cache/internal/constants.ts +45 -0
  204. package/src/stubs/cache/internal/tar.ts +374 -0
  205. package/src/stubs/cache/options.ts +91 -0
  206. package/src/stubs/core/core.ts +743 -0
  207. package/src/stubs/core/path-utils.ts +39 -0
  208. package/src/stubs/core/platform.ts +132 -0
  209. package/src/stubs/core/summary.ts +399 -0
  210. package/src/stubs/env.ts +40 -0
  211. package/src/stubs/github/context.ts +94 -0
  212. package/src/stubs/github/github.ts +29 -0
  213. package/src/stubs/github/interfaces.ts +52 -0
  214. package/src/stubs/github/internal/utils.ts +83 -0
  215. package/src/stubs/github/utils.ts +52 -0
  216. package/src/types/quibble.d.ts +5 -0
  217. package/src/types.ts +116 -0
  218. package/src/utils/config.ts +47 -0
  219. package/src/utils/output.ts +20 -0
  220. package/src/utils/package.ts +38 -0
  221. package/tsconfig.base.json +29 -0
  222. package/tsconfig.eslint.json +22 -0
  223. package/tsconfig.json +18 -0
@@ -0,0 +1,760 @@
1
+ import { jest } from '@jest/globals'
2
+ import {
3
+ CoreMeta,
4
+ ResetCoreMetadata,
5
+ addPath,
6
+ debug,
7
+ endGroup,
8
+ error,
9
+ exportVariable,
10
+ getBooleanInput,
11
+ getIDToken,
12
+ getInput,
13
+ getMultilineInput,
14
+ getState,
15
+ group,
16
+ info,
17
+ isDebug,
18
+ log,
19
+ notice,
20
+ saveState,
21
+ setCommandEcho,
22
+ setFailed,
23
+ setOutput,
24
+ setSecret,
25
+ startGroup,
26
+ warning
27
+ } from '../../../src/stubs/core/core.js'
28
+ import { EnvMeta, ResetEnvMetadata } from '../../../src/stubs/env.js'
29
+ import type { CoreMetadata } from '../../../src/types.js'
30
+
31
+ /** Empty CoreMetadata Object */
32
+ const empty: CoreMetadata = {
33
+ exitCode: 0,
34
+ exitMessage: '',
35
+ outputs: {},
36
+ secrets: [],
37
+ stepDebug: process.env.ACTIONS_STEP_DEBUG === 'true',
38
+ stepSummaryPath: process.env.GITHUB_STEP_SUMMARY ?? '',
39
+ echo: false,
40
+ state: {},
41
+ colors: {
42
+ cyan: console.log,
43
+ blue: console.log,
44
+ gray: console.log,
45
+ green: console.log,
46
+ magenta: console.log,
47
+ red: console.log,
48
+ white: console.log,
49
+ yellow: console.log
50
+ }
51
+ }
52
+
53
+ // Prevent output during tests
54
+ jest.spyOn(console, 'log').mockImplementation(() => {})
55
+ jest.spyOn(console, 'table').mockImplementation(() => {})
56
+
57
+ describe('Core', () => {
58
+ beforeEach(() => {
59
+ // Reset metadata
60
+ ResetEnvMetadata()
61
+ ResetCoreMetadata()
62
+ })
63
+
64
+ afterEach(() => {
65
+ jest.resetAllMocks()
66
+ })
67
+
68
+ describe('CoreMeta', () => {
69
+ it('Logs using colors', () => {
70
+ CoreMeta.colors.cyan('cyan')
71
+ CoreMeta.colors.blue('blue')
72
+ CoreMeta.colors.gray('gray')
73
+ CoreMeta.colors.green('green')
74
+ CoreMeta.colors.magenta('magenta')
75
+ CoreMeta.colors.red('red')
76
+ CoreMeta.colors.white('white')
77
+ CoreMeta.colors.yellow('yellow')
78
+
79
+ expect(console.log).toHaveBeenCalledWith('cyan')
80
+ expect(console.log).toHaveBeenCalledWith('blue')
81
+ expect(console.log).toHaveBeenCalledWith('gray')
82
+ expect(console.log).toHaveBeenCalledWith('green')
83
+ expect(console.log).toHaveBeenCalledWith('magenta')
84
+ expect(console.log).toHaveBeenCalledWith('red')
85
+ expect(console.log).toHaveBeenCalledWith('white')
86
+ expect(console.log).toHaveBeenCalledWith('yellow')
87
+ })
88
+
89
+ it('Tracks updates to the core metadata', () => {
90
+ // Initial state should be empty
91
+ expect(CoreMeta.exitCode).toEqual(empty.exitCode)
92
+ expect(CoreMeta.exitMessage).toEqual(empty.exitMessage)
93
+ expect(CoreMeta.outputs).toMatchObject(empty.outputs)
94
+ expect(CoreMeta.secrets).toMatchObject(empty.secrets)
95
+ expect(CoreMeta.stepDebug).toEqual(empty.stepDebug)
96
+ expect(CoreMeta.stepSummaryPath).toEqual(empty.stepSummaryPath)
97
+ expect(CoreMeta.echo).toEqual(empty.echo)
98
+ expect(CoreMeta.state).toMatchObject(empty.state)
99
+
100
+ // Update the metadata
101
+ CoreMeta.exitCode = 1
102
+ CoreMeta.exitMessage = 'test'
103
+ CoreMeta.outputs = { 'my-output': 'test' }
104
+ CoreMeta.secrets = ['secret-value-1234']
105
+ CoreMeta.stepDebug = true
106
+ CoreMeta.stepSummaryPath = 'test'
107
+ CoreMeta.echo = true
108
+ CoreMeta.state = { 'my-state': 'test' }
109
+
110
+ // Verify the updated metadata
111
+ expect(CoreMeta.exitCode).toEqual(1)
112
+ expect(CoreMeta.exitMessage).toEqual('test')
113
+ expect(CoreMeta.outputs).toMatchObject({ 'my-output': 'test' })
114
+ expect(CoreMeta.secrets).toMatchObject(['secret-value-1234'])
115
+ expect(CoreMeta.stepDebug).toEqual(true)
116
+ expect(CoreMeta.stepSummaryPath).toEqual('test')
117
+ expect(CoreMeta.echo).toEqual(true)
118
+ expect(CoreMeta.state).toMatchObject({ 'my-state': 'test' })
119
+
120
+ // Reset the metadata
121
+ ResetCoreMetadata()
122
+
123
+ // Verify the reset metadata
124
+ expect(CoreMeta.exitCode).toEqual(empty.exitCode)
125
+ expect(CoreMeta.exitMessage).toEqual(empty.exitMessage)
126
+ expect(CoreMeta.outputs).toMatchObject(empty.outputs)
127
+ expect(CoreMeta.secrets).toMatchObject(empty.secrets)
128
+ expect(CoreMeta.stepDebug).toEqual(empty.stepDebug)
129
+ expect(CoreMeta.stepSummaryPath).toEqual(empty.stepSummaryPath)
130
+ expect(CoreMeta.echo).toEqual(empty.echo)
131
+ expect(CoreMeta.state).toMatchObject(empty.state)
132
+ })
133
+
134
+ it('Defaults stepSummaryPath to an empty string', () => {
135
+ delete process.env.GITHUB_STEP_SUMMARY
136
+
137
+ ResetCoreMetadata()
138
+ expect(CoreMeta.stepSummaryPath).toEqual('')
139
+ })
140
+
141
+ it('Sets stepSummaryPath from the environment', () => {
142
+ process.env.GITHUB_STEP_SUMMARY = 'summary.md'
143
+
144
+ ResetCoreMetadata()
145
+ expect(CoreMeta.stepSummaryPath).toEqual('summary.md')
146
+ })
147
+ })
148
+
149
+ describe('Core Stubs', () => {
150
+ describe('exportVariable()', () => {
151
+ it('Exports an environment variable', () => {
152
+ exportVariable('TEST', 'test')
153
+ expect(EnvMeta.env).toMatchObject({ TEST: 'test' })
154
+ })
155
+
156
+ it('Exports undefined and null as empty strings', () => {
157
+ exportVariable('UNDEFINED_TEST', undefined)
158
+ exportVariable('NULL_TEST', undefined)
159
+ expect(EnvMeta.env).toMatchObject({ UNDEFINED_TEST: '', NULL_TEST: '' })
160
+ })
161
+
162
+ it('Exports objects as stringified JSON', () => {
163
+ const testVariable = {
164
+ my_key: 'my_value'
165
+ }
166
+
167
+ exportVariable('TEST', testVariable)
168
+ expect(EnvMeta.env).toMatchObject({
169
+ TEST: JSON.stringify(testVariable)
170
+ })
171
+ })
172
+ })
173
+
174
+ describe('setSecre()', () => {
175
+ it('Sets a secret to mask', () => {
176
+ setSecret('test')
177
+ expect(CoreMeta.secrets).toMatchObject(['test'])
178
+ })
179
+ })
180
+
181
+ describe('addPath()', () => {
182
+ it('Appends to the path', () => {
183
+ addPath('/usr/test')
184
+ expect(EnvMeta.path.includes('/usr/test')).toBeTruthy()
185
+ })
186
+ })
187
+
188
+ describe('getInput()', () => {
189
+ it('Gets action inputs', () => {
190
+ // Test both upper and lower case versions of the input
191
+ process.env.INPUT_TEST = 'test-upper'
192
+ expect(getInput('test')).toEqual('test-upper')
193
+
194
+ delete process.env.INPUT_TEST
195
+
196
+ process.env.INPUT_test = 'test-lower'
197
+ expect(getInput('test')).toEqual('test-lower')
198
+ })
199
+
200
+ it('Gets default inputs', () => {
201
+ delete process.env.INPUT_TEST
202
+ delete process.env.INPUT_test
203
+
204
+ EnvMeta.inputs = {
205
+ test: {
206
+ description: 'test',
207
+ required: true,
208
+ default: 'default'
209
+ }
210
+ }
211
+
212
+ expect(getInput('test')).toEqual('default')
213
+ })
214
+
215
+ it('Returns an empty string', () => {
216
+ expect(getInput('test-input-missing')).toEqual('')
217
+ })
218
+
219
+ it('Throws an error if the input is required and not found', () => {
220
+ expect(() =>
221
+ getInput('test-input-missing', { required: true })
222
+ ).toThrow()
223
+ })
224
+
225
+ it('Trims whitespace', () => {
226
+ process.env.INPUT_TEST = ' test '
227
+ expect(getInput('test', { trimWhitespace: true })).toEqual('test')
228
+ })
229
+ })
230
+
231
+ describe('getMultilineInput()', () => {
232
+ it('Gets action inputs', () => {
233
+ // Test both upper and lower case versions of the input
234
+ process.env.INPUT_TEST = `test\nmultiline\nupper`
235
+ expect(getMultilineInput('test')).toMatchObject([
236
+ 'test',
237
+ 'multiline',
238
+ 'upper'
239
+ ])
240
+
241
+ delete process.env.INPUT_TEST
242
+
243
+ process.env.INPUT_test = `test\nmultiline\nlower`
244
+ expect(getMultilineInput('test')).toMatchObject([
245
+ 'test',
246
+ 'multiline',
247
+ 'lower'
248
+ ])
249
+ })
250
+
251
+ it('Gets default inputs', () => {
252
+ delete process.env.INPUT_TEST
253
+ delete process.env.INPUT_test
254
+
255
+ EnvMeta.inputs = {
256
+ test: {
257
+ description: 'test',
258
+ required: true,
259
+ default: 'default'
260
+ }
261
+ }
262
+
263
+ expect(getMultilineInput('test')).toEqual(['default'])
264
+ })
265
+
266
+ it('Returns an empty list if the input is not found', () => {
267
+ expect(getMultilineInput('test-input-missing')).toMatchObject([])
268
+ })
269
+
270
+ it('Throws an error if the input is required and not found', () => {
271
+ expect(() =>
272
+ getMultilineInput('test-input-missing', {
273
+ required: true
274
+ })
275
+ ).toThrow()
276
+ })
277
+
278
+ it('Trims whitespace from the input', () => {
279
+ process.env.INPUT_TEST = ' test \n muliline \n spaces '
280
+ expect(
281
+ getMultilineInput('test', { trimWhitespace: true })
282
+ ).toMatchObject(['test', 'muliline', 'spaces'])
283
+ })
284
+ })
285
+
286
+ describe('getBooleanInput()', () => {
287
+ it('Gets the action inputs', () => {
288
+ // Test both upper and lower case versions of the input
289
+ process.env.INPUT_TEST = 'true'
290
+ expect(getBooleanInput('test')).toBeTruthy()
291
+
292
+ delete process.env.INPUT_TEST
293
+
294
+ process.env.INPUT_test = 'false'
295
+ expect(getBooleanInput('test')).toBeFalsy()
296
+ })
297
+
298
+ it('Gets default inputs', () => {
299
+ delete process.env.INPUT_TEST
300
+ delete process.env.INPUT_test
301
+
302
+ EnvMeta.inputs = {
303
+ test: {
304
+ description: 'test',
305
+ required: true,
306
+ default: 'false'
307
+ }
308
+ }
309
+
310
+ expect(getBooleanInput('test')).toEqual(false)
311
+ })
312
+
313
+ it('Gets default inputs - with an unquoted boolean', () => {
314
+ delete process.env.INPUT_TEST
315
+ delete process.env.INPUT_test
316
+
317
+ EnvMeta.inputs = {
318
+ test: {
319
+ description: 'test',
320
+ required: true,
321
+ // while the spec says that the default value should be a string
322
+ // the yaml parser will pass an unquoted `true` or `false` through
323
+ // as a boolean
324
+ default: false as any
325
+ }
326
+ }
327
+
328
+ expect(getBooleanInput('test')).toEqual(false)
329
+ })
330
+
331
+ it('Throws an error if the input is required and not found', () => {
332
+ expect(() =>
333
+ getBooleanInput('test-input-missing', {
334
+ required: true
335
+ })
336
+ ).toThrow()
337
+ })
338
+ it('Throws an error if the input is NOT required and not found', () => {
339
+ // ideally this would not throw - and either coerce to false or return
340
+ // undefined but this will require upstream changes. See discussion at
341
+ // https://github.com/github/local-action/pull/140
342
+ expect(() =>
343
+ getBooleanInput('test-input-missing', {
344
+ required: false
345
+ })
346
+ ).toThrow()
347
+ })
348
+
349
+ it('Returns true or false for valid YAML boolean values', () => {
350
+ process.env.INPUT_TEST = 'true'
351
+ expect(getBooleanInput('test')).toBeTruthy()
352
+
353
+ process.env.INPUT_TEST = 'True'
354
+ expect(getBooleanInput('test')).toBeTruthy()
355
+
356
+ process.env.INPUT_TEST = 'TRUE'
357
+ expect(getBooleanInput('test')).toBeTruthy()
358
+
359
+ process.env.INPUT_TEST = 'false'
360
+ expect(getBooleanInput('test')).toBeFalsy()
361
+
362
+ process.env.INPUT_TEST = 'False'
363
+ expect(getBooleanInput('test')).toBeFalsy()
364
+
365
+ process.env.INPUT_TEST = 'FALSE'
366
+ expect(getBooleanInput('test')).toBeFalsy()
367
+ })
368
+
369
+ it('Throws an error if the input is not a valid YAML boolean value', () => {
370
+ process.env.INPUT_TEST = 'This is not a valid boolean value'
371
+ expect(() => getBooleanInput('test')).toThrow()
372
+ })
373
+ })
374
+
375
+ describe('setOutput()', () => {
376
+ it('Sets the action outputs', () => {
377
+ jest.spyOn(CoreMeta.colors, 'cyan').mockImplementation(() => {})
378
+
379
+ setOutput('string-output', 'output-value')
380
+ setOutput('json-output', {
381
+ 'my-output': 'output-value'
382
+ })
383
+ setOutput('array-output', ['output-value'])
384
+ setOutput('undefined-output', undefined)
385
+ setOutput('null-output', null)
386
+ setOutput('empty-output', '')
387
+ setOutput('number-output', 123)
388
+ setOutput('boolean-output', true)
389
+
390
+ expect(CoreMeta.outputs['string-output']).toEqual('output-value')
391
+ expect(CoreMeta.outputs['json-output']).toEqual(
392
+ JSON.stringify({ 'my-output': 'output-value' })
393
+ )
394
+ expect(CoreMeta.outputs['array-output']).toEqual(
395
+ JSON.stringify(['output-value'])
396
+ )
397
+ expect(CoreMeta.outputs['undefined-output']).toEqual('')
398
+ expect(CoreMeta.outputs['null-output']).toEqual('')
399
+ expect(CoreMeta.outputs['empty-output']).toEqual('')
400
+ expect(CoreMeta.outputs['number-output']).toEqual('123')
401
+ expect(CoreMeta.outputs['boolean-output']).toEqual('true')
402
+ })
403
+
404
+ it('Does not set an output that contains a secret', () => {
405
+ const core_warningSpy = jest
406
+ .spyOn(CoreMeta.colors, 'yellow')
407
+ .mockImplementation(() => {})
408
+
409
+ CoreMeta.secrets = ['output-value']
410
+
411
+ setOutput('my-output', 'output-value')
412
+
413
+ expect(core_warningSpy).toHaveBeenCalledWith(
414
+ '::warning::Skip output my-output since it may contain secret.'
415
+ )
416
+ expect(CoreMeta.outputs['my-output']).toEqual(undefined)
417
+ })
418
+
419
+ it('Logs the output to the console', () => {
420
+ const core_outputSpy = jest
421
+ .spyOn(CoreMeta.colors, 'cyan')
422
+ .mockImplementation(() => {})
423
+
424
+ setOutput('my-output', 'output-value')
425
+
426
+ expect(core_outputSpy).toHaveBeenCalledWith(
427
+ '::set-output name=my-output::output-value'
428
+ )
429
+ })
430
+ })
431
+
432
+ describe('setCommandEcho()', () => {
433
+ it('Sets the command echo flag', () => {
434
+ setCommandEcho(true)
435
+ expect(CoreMeta.echo).toBeTruthy()
436
+
437
+ setCommandEcho(false)
438
+ expect(CoreMeta.echo).toBeFalsy()
439
+ })
440
+ })
441
+
442
+ describe('setFailed()', () => {
443
+ it('Sets the exit code to failure', () => {
444
+ jest.spyOn(CoreMeta.colors, 'red').mockImplementation(() => {})
445
+
446
+ setFailed('test')
447
+
448
+ expect(CoreMeta.exitCode).toEqual(1)
449
+ expect(CoreMeta.exitMessage).toEqual('test')
450
+ })
451
+ })
452
+
453
+ describe('log()', () => {
454
+ it('Defaults to gray if no color is provided', () => {
455
+ log('invalid-type', 'test')
456
+
457
+ expect(console.log).toHaveBeenCalledWith('::invalid-type::test')
458
+ })
459
+
460
+ it('Throws an error if startLine and endLine are different when columns are set', () => {
461
+ expect((): void =>
462
+ log('group', 'my message', {
463
+ startLine: 1,
464
+ endLine: 2,
465
+ startColumn: 1
466
+ })
467
+ ).toThrow()
468
+
469
+ expect((): void =>
470
+ log('group', 'my message', {
471
+ startLine: 1,
472
+ endLine: 2,
473
+ endColumn: 2
474
+ })
475
+ ).toThrow()
476
+ })
477
+
478
+ it('Logs only the color when no message is provided', () => {
479
+ const core_outputSpy = jest
480
+ .spyOn(CoreMeta.colors, 'blue')
481
+ .mockImplementation(() => {})
482
+
483
+ log('group')
484
+
485
+ expect(core_outputSpy).toHaveBeenCalledWith('::group::')
486
+ })
487
+
488
+ it('Redacts secrets from the output', () => {
489
+ const core_outputSpy = jest
490
+ .spyOn(CoreMeta.colors, 'blue')
491
+ .mockImplementation(() => {})
492
+
493
+ // Set a secret to mask
494
+ CoreMeta.secrets = ['secret-value-1234']
495
+
496
+ log('group', 'my secret is secret-value-1234')
497
+
498
+ expect(core_outputSpy).toHaveBeenCalledWith(
499
+ '::group::my secret is ****'
500
+ )
501
+ })
502
+
503
+ it('Includes annotations in the output', () => {
504
+ const core_outputSpy = jest
505
+ .spyOn(CoreMeta.colors, 'blue')
506
+ .mockImplementation(() => {})
507
+
508
+ log('group', 'my message', {
509
+ title: 'my title',
510
+ file: 'my-file.txt'
511
+ })
512
+
513
+ expect(core_outputSpy).toHaveBeenCalledWith(
514
+ '::group title=my title,file=my-file.txt::my message'
515
+ )
516
+ })
517
+
518
+ it('Defaults the endLine property to startLine', () => {
519
+ const core_outputSpy = jest
520
+ .spyOn(CoreMeta.colors, 'white')
521
+ .mockImplementation(() => {})
522
+
523
+ log('info', 'my message', {
524
+ startLine: 1
525
+ })
526
+
527
+ expect(core_outputSpy).toHaveBeenCalledWith(
528
+ '::info line=1,endLine=1::my message'
529
+ )
530
+ })
531
+
532
+ it('Defaults the endColumn property to startColumn', () => {
533
+ const core_outputSpy = jest
534
+ .spyOn(CoreMeta.colors, 'white')
535
+ .mockImplementation(() => {})
536
+
537
+ log('info', 'my message', {
538
+ startColumn: 1
539
+ })
540
+
541
+ expect(core_outputSpy).toHaveBeenCalledWith(
542
+ '::info col=1,endColumn=1::my message'
543
+ )
544
+ })
545
+ })
546
+
547
+ describe('isDebug()', () => {
548
+ it('Returns the step debug setting', () => {
549
+ CoreMeta.stepDebug = true
550
+ expect(isDebug()).toBeTruthy()
551
+
552
+ CoreMeta.stepDebug = false
553
+ expect(isDebug()).toBeFalsy()
554
+ })
555
+ })
556
+
557
+ describe('debug()', () => {
558
+ it('Logs to the console if debug logging is enabled', () => {
559
+ // Enable step debug logging
560
+ CoreMeta.stepDebug = true
561
+
562
+ const core_outputSpy = jest
563
+ .spyOn(CoreMeta.colors, 'gray')
564
+ .mockImplementation(() => {})
565
+
566
+ debug('test')
567
+
568
+ expect(core_outputSpy).toHaveBeenCalledWith('::debug::test')
569
+ })
570
+
571
+ it('Does not log to the console if debug logging is disabled', () => {
572
+ // Disable step debug logging
573
+ CoreMeta.stepDebug = false
574
+
575
+ const core_outputSpy = jest
576
+ .spyOn(CoreMeta.colors, 'gray')
577
+ .mockImplementation(() => {})
578
+
579
+ debug('test')
580
+
581
+ expect(core_outputSpy).not.toHaveBeenCalled()
582
+ })
583
+ })
584
+
585
+ describe('error()', () => {
586
+ it('Logs to the console', () => {
587
+ const core_outputSpy = jest
588
+ .spyOn(CoreMeta.colors, 'red')
589
+ .mockImplementation(() => {})
590
+
591
+ error('test')
592
+
593
+ expect(core_outputSpy).toHaveBeenCalledWith('::error::test')
594
+ })
595
+
596
+ it('Logs to the console (Error)', () => {
597
+ const core_outputSpy = jest
598
+ .spyOn(CoreMeta.colors, 'red')
599
+ .mockImplementation(() => {})
600
+
601
+ error(new Error('test'))
602
+
603
+ expect(core_outputSpy).toHaveBeenCalledWith('::error::Error: test')
604
+ })
605
+ })
606
+
607
+ describe('warning()', () => {
608
+ it('Logs to the console', () => {
609
+ const core_outputSpy = jest
610
+ .spyOn(CoreMeta.colors, 'yellow')
611
+ .mockImplementation(() => {})
612
+
613
+ warning('test')
614
+
615
+ expect(core_outputSpy).toHaveBeenCalledWith('::warning::test')
616
+ })
617
+
618
+ it('Logs to the console (Error)', () => {
619
+ const core_outputSpy = jest
620
+ .spyOn(CoreMeta.colors, 'yellow')
621
+ .mockImplementation(() => {})
622
+
623
+ warning(new Error('test'))
624
+
625
+ expect(core_outputSpy).toHaveBeenCalledWith('::warning::Error: test')
626
+ })
627
+ })
628
+
629
+ describe('notice()', () => {
630
+ it('Logs to the console', () => {
631
+ const core_outputSpy = jest
632
+ .spyOn(CoreMeta.colors, 'magenta')
633
+ .mockImplementation(() => {})
634
+
635
+ notice('test')
636
+
637
+ expect(core_outputSpy).toHaveBeenCalledWith('::notice::test')
638
+ })
639
+
640
+ it('Logs to the console (Error)', () => {
641
+ const core_outputSpy = jest
642
+ .spyOn(CoreMeta.colors, 'magenta')
643
+ .mockImplementation(() => {})
644
+
645
+ notice(new Error('test'))
646
+
647
+ expect(core_outputSpy).toHaveBeenCalledWith('::notice::Error: test')
648
+ })
649
+ })
650
+
651
+ describe('info()', () => {
652
+ it('Logs to the console', () => {
653
+ const core_outputSpy = jest
654
+ .spyOn(CoreMeta.colors, 'white')
655
+ .mockImplementation(() => {})
656
+
657
+ info('test')
658
+
659
+ expect(core_outputSpy).toHaveBeenCalledWith('::info::test')
660
+ })
661
+ })
662
+
663
+ describe('startGroup()', () => {
664
+ it('Logs to the console', () => {
665
+ const core_outputSpy = jest
666
+ .spyOn(CoreMeta.colors, 'blue')
667
+ .mockImplementation(() => {})
668
+
669
+ startGroup('test')
670
+
671
+ expect(core_outputSpy).toHaveBeenCalledWith('::group::test')
672
+ })
673
+ })
674
+
675
+ describe('endGroup()', () => {
676
+ it('Logs to the console', () => {
677
+ const core_outputSpy = jest
678
+ .spyOn(CoreMeta.colors, 'blue')
679
+ .mockImplementation(() => {})
680
+
681
+ endGroup()
682
+
683
+ expect(core_outputSpy).toHaveBeenCalledWith('::endgroup::')
684
+ })
685
+ })
686
+
687
+ describe('group()', () => {
688
+ it('Logs grouped messages to the console', async () => {
689
+ const core_outputSpy = jest
690
+ .spyOn(CoreMeta.colors, 'blue')
691
+ .mockImplementation(() => {})
692
+
693
+ const core_infoSpy = jest.spyOn(CoreMeta.colors, 'white')
694
+
695
+ await group('my-group', async () => {
696
+ info('test')
697
+
698
+ await Promise.resolve()
699
+
700
+ return
701
+ })
702
+
703
+ expect(core_outputSpy).toHaveBeenCalledWith('::group::my-group')
704
+ expect(core_infoSpy).toHaveBeenCalledWith('::info::test')
705
+ expect(core_outputSpy).toHaveBeenCalledWith('::endgroup::')
706
+ })
707
+ })
708
+
709
+ describe('saveState()', () => {
710
+ it('Saves string data', () => {
711
+ saveState(
712
+ 'string-test',
713
+ JSON.stringify({ 'my-state': 'test-string-info' })
714
+ )
715
+
716
+ expect(CoreMeta.state).toMatchObject({
717
+ 'string-test': '{"my-state":"test-string-info"}'
718
+ })
719
+ })
720
+
721
+ it('Saves JSON data', () => {
722
+ saveState('json-test', { 'my-state': 'test-json-info' })
723
+
724
+ expect(CoreMeta.state).toMatchObject({
725
+ 'json-test': '{"my-state":"test-json-info"}'
726
+ })
727
+ })
728
+
729
+ it('Saves null and undefined as empty strings', () => {
730
+ saveState('undefined-test', undefined)
731
+ saveState('null-test', null)
732
+
733
+ expect(CoreMeta.state).toMatchObject({
734
+ 'undefined-test': '',
735
+ 'null-test': ''
736
+ })
737
+ })
738
+ })
739
+
740
+ describe('getState()', () => {
741
+ it('Gets the state from the environment', () => {
742
+ CoreMeta.state = {
743
+ test: '{"my-state":"test-info"}'
744
+ }
745
+
746
+ expect(getState('test')).toEqual('{"my-state":"test-info"}')
747
+ })
748
+
749
+ it('Returns an empty string for values not in the state', () => {
750
+ expect(getState('nonexistent-test')).toEqual('')
751
+ })
752
+ })
753
+
754
+ describe('getIDToken()', () => {
755
+ it('Throws an error', async () => {
756
+ await expect(getIDToken()).rejects.toThrow('Not implemented')
757
+ })
758
+ })
759
+ })
760
+ })