diff --git a/README.md b/README.md index 7ea6721..4bc0437 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ _**Note:** The examples below use version `0.1.0` of `deno-slack-builder`; check In a directory that contains a valid manifest file (`manifest.json`, `manifest.js`, or `manifest.ts`), run the following: -``` +```bash deno run --allow-write --allow-read "https://deno.land/x/deno_slack_builder@0.1.0/mod.ts" ``` @@ -31,12 +31,14 @@ The top level `mod.ts` file is executed as a Deno program, and takes up to three ### Example Usage **Only generate a valid Run On Slack manifest file:** -``` + +```bash deno run --allow-write --allow-read "https://deno.land/x/deno_slack_builder@0.1.0/mod.ts" --manifest ``` **Generate a Run On Slack project from a /src directory:** -``` + +```bash deno run --allow-write --allow-read "https://deno.land/x/deno_slack_builder@0.1.0/mod.ts" --source src ``` @@ -55,6 +57,7 @@ Allows for flexibility with how you define your manifest. * If no `manifest.ts` exists, looks for a `manifest.js` file, and follows the same logic as `manifest.ts` does. ## Function Bundling + * For each entry in the `functions` where `remote_environment=slack` it looks for a `source_file` property, which should be a relative path to the corresponding function file. This is then bundled for the Run on Slack Deno runtime. The `reverse` function defined below indicates there should be a corresponding function file in the project located at `functions/reverse.ts`. ```json @@ -89,11 +92,15 @@ Allows for flexibility with how you define your manifest. If you make changes to this repo, or just want to make sure things are working as desired, you can run: - deno task test +```bash +deno task test +``` To get a full test coverage report, run: - deno task coverage +```bash +deno task coverage +``` --- diff --git a/deno.jsonc b/deno.jsonc index de21973..81bb3cc 100644 --- a/deno.jsonc +++ b/deno.jsonc @@ -10,7 +10,7 @@ } }, "tasks": { - "test": "deno fmt --check && deno lint && deno test src", - "coverage": "deno test --coverage=.coverage src && deno coverage --exclude=fixtures --exclude=test .coverage" + "test": "deno fmt --check && deno lint && deno test --allow-read src", + "coverage": "deno test --coverage=.coverage --allow-read src && deno coverage --exclude=fixtures --exclude=test .coverage" } } diff --git a/src/dev_deps.ts b/src/dev_deps.ts index 510fbda..6cd67c4 100644 --- a/src/dev_deps.ts +++ b/src/dev_deps.ts @@ -1,4 +1,5 @@ export { assertEquals, assertExists, + assertRejects, } from "https://deno.land/std@0.134.0/testing/asserts.ts"; diff --git a/src/functions.ts b/src/functions.ts index edc6ab4..611bd50 100644 --- a/src/functions.ts +++ b/src/functions.ts @@ -42,6 +42,15 @@ export const validateAndCreateFunctions = async ( } }; +const functionPathHasDefaultExport = async ( + functionFilePath: string, +) => { + const functionModule = await import(`file://${functionFilePath}`); + return functionModule.default + ? typeof functionModule.default == "function" + : false; +}; + const getValidFunctionPath = async ( options: Options, fnId: string, @@ -70,6 +79,12 @@ const getValidFunctionPath = async ( } throw new Error(e); } + + if (!await functionPathHasDefaultExport(fnFilePath)) { + throw new Error( + `File: ${fnFilePath}, containing your function does not define a default export handler.`, + ); + } return fnFilePath; }; diff --git a/src/tests/functions/test_function_file.ts b/src/tests/functions/test_function_file.ts new file mode 100644 index 0000000..2dec3c3 --- /dev/null +++ b/src/tests/functions/test_function_file.ts @@ -0,0 +1,3 @@ +export default () => { + console.log("this is my slack function"); +}; diff --git a/src/tests/functions/test_function_no_export_file.ts b/src/tests/functions/test_function_no_export_file.ts new file mode 100644 index 0000000..c5de320 --- /dev/null +++ b/src/tests/functions/test_function_no_export_file.ts @@ -0,0 +1,3 @@ +export const func = () => { + console.log("this is my slack function"); +}; diff --git a/src/tests/functions/test_function_not_function_file.ts b/src/tests/functions/test_function_not_function_file.ts new file mode 100644 index 0000000..34b58e6 --- /dev/null +++ b/src/tests/functions/test_function_not_function_file.ts @@ -0,0 +1 @@ +export default "hello"; diff --git a/src/tests/functions_test.ts b/src/tests/functions_test.ts index 670ba6c..7505467 100644 --- a/src/tests/functions_test.ts +++ b/src/tests/functions_test.ts @@ -1,6 +1,107 @@ import { validateAndCreateFunctions } from "../functions.ts"; -import { assertExists } from "../dev_deps.ts"; +import { assertEquals, assertExists, assertRejects } from "../dev_deps.ts"; +import { Options } from "../types.ts"; Deno.test("validateAndCreateFunctions", () => { assertExists(validateAndCreateFunctions); }); + +Deno.test("validateAndCreateFunctions should not throw an exception when fed a function file that has a default export", async () => { + const captured_log: string[] = []; + const options: Options = { + manifestOnly: true, + workingDirectory: Deno.cwd(), + log: (x) => { + captured_log.push(x); + }, + }; + const manifest = { + "functions": { + "test_function": { + "title": "Test function", + "description": "this is a test", + "source_file": "src/tests/functions/test_function_file.ts", + "input_parameters": { + "required": [], + "properties": {}, + }, + "output_parameters": { + "required": [], + "properties": {}, + }, + }, + }, + }; + + await validateAndCreateFunctions( + options, + manifest, + ); + assertEquals("", captured_log.join("")); +}); + +Deno.test("Function files with no default export should get an error", async () => { + const captured_log: string[] = []; + const options: Options = { + manifestOnly: true, + workingDirectory: Deno.cwd(), + log: (x) => { + captured_log.push(x); + }, + }; + const manifest = { + "functions": { + "test_function": { + "title": "Test function", + "description": "this is a test", + "source_file": "src/tests/functions/test_function_no_export_file.ts", + "input_parameters": { + "required": [], + "properties": {}, + }, + "output_parameters": { + "required": [], + "properties": {}, + }, + }, + }, + }; + await assertRejects( + () => validateAndCreateFunctions(options, manifest), + Error, + "default export handler", + ); +}); + +Deno.test("Function files not exporting a function should get an error", async () => { + const captured_log: string[] = []; + const options: Options = { + manifestOnly: true, + workingDirectory: Deno.cwd(), + log: (x) => { + captured_log.push(x); + }, + }; + const manifest = { + "functions": { + "test_function": { + "title": "Test function", + "description": "this is a test", + "source_file": "src/tests/functions/test_function_not_function_file.ts", + "input_parameters": { + "required": [], + "properties": {}, + }, + "output_parameters": { + "required": [], + "properties": {}, + }, + }, + }, + }; + await assertRejects( + () => validateAndCreateFunctions(options, manifest), + Error, + "default export handler", + ); +});