└── README.md /README.md: -------------------------------------------------------------------------------- 1 | # sst 2 | 3 | Super Simple Test Format 4 | 5 | This a definition/spec for very simple and portable JavaScript tests. 6 | 7 | The goal of this spec is to separate the test format from the runner/framework. 8 | 9 | # Basics 10 | 11 | * A given JavaScript module (ESM or CommonJS depending on your environment) is the top level container for tests. 12 | * All tests are functions. 13 | * All exported functions are tests. 14 | * A module may export multiple test functions or a single default export function as its only test. 15 | 16 | ## Advanced 17 | 18 | * If the exports contain a `parallel` property set to `true` then the tests can be running in parallel. 19 | * Otherwise, test ordering must be preserved from the original definition. 20 | * A test function *may* have a "testName" property. This name will be used instead of the exported function name 21 | during reporting. 22 | * That's it! 23 | 24 | ## Motivations / Goals 25 | 26 | * In order to write tests that run easily across different environments (Node.js, Browsers, etc) we need 27 | to separate the test runner (platform specific) from the tests themselves (plain JavaScript). 28 | * Lay the groundwork for "Universal JavaScript Tests" (tests that can run in browsers, Node.js, and future 29 | JS platforms without a compiler. 30 | * Enable people to write tests that others can safely run in a browser sandbox. This is useful for running 31 | all the tests of dependent projects before updating a widely used dependency. 32 | 33 | A few constraints imposed by these goals. 34 | 35 | * No callback based tests. Sync functions are considered sync tests and async functions are async tests. 36 | * Exceptions must be the only way to fail a test. 37 | * No metadata about the tests can be "out of band" in package.json or elsewhere because this presumes a 38 | compiler and will not work for native ESM in the browser. 39 | 40 | ## Test Examples 41 | 42 | Single exports 43 | 44 | ```javascript 45 | /* commonjs */ 46 | const assert = require('assert') 47 | module.exports = () => assert(true, true) 48 | 49 | /* esm */ 50 | export default () => assert(true, true) 51 | ``` 52 | 53 | Multiple exports 54 | 55 | ```javascript 56 | /* commonjs */ 57 | exports.testPass = async () => { /* noop */ } 58 | exports.testFail = async () => throw new Error('fail please') 59 | 60 | /* esm */ 61 | export async () => { /* noop */ } as testPass 62 | export async () => throw new Error('fail please') as testFail 63 | ``` 64 | 65 | Name overwrites 66 | 67 | ```javascript 68 | exports.testHttp = async () => { 69 | /* some http test code */ 70 | } 71 | exports.testHttp.testName = 'http 200 works properly' 72 | 73 | /* or, an easier api would be */ 74 | 75 | const test = (name, t) => { 76 | t.testName = name 77 | return t 78 | } 79 | 80 | exports.testHttp = test('http 200 works property', async () => { 81 | /* some http test code */ 82 | } 83 | ``` 84 | 85 | ## How to implement more advanced features. 86 | 87 | Common operations like `setup` and `teardown` can be wrapped around each test function by 88 | any number of test frameworks. 89 | 90 | ```javascript 91 | /* some-test-framwork */ 92 | module.exports = obj => async () => { 93 | if (obj.setup) await obj.setup() 94 | await obj.run() 95 | if (obj.teardown) await obj.teardown() 96 | } 97 | 98 | /* test.js */ 99 | const test = require('some-test-framework') 100 | const assert = require('assert') 101 | 102 | let global 103 | exports.myTest = test({setup: () => { global = 'run' }, test: () => assert(global, 'run')}) 104 | ``` 105 | --------------------------------------------------------------------------------