=>
new Promise((resolve) => server.close(() => resolve(undefined)));
-describe('Wait For Port', async () => {
- await kill.range(8000, 8003);
+test('Wait For Port', async () => {
+ try {
+ await kill.range(8000, 8003);
+ } catch {}
await Promise.all([
it(async () => {
@@ -60,7 +61,7 @@ describe('Wait For Port', async () => {
} catch (error) {
assert.strictEqual(
(error as Error).message,
- `Timeout waiting for port ${port} to become active`,
+ `Timeout`,
'Expected timeout for missing port'
);
}
@@ -80,19 +81,6 @@ describe('Wait For Port', async () => {
}
}),
- it(async () => {
- const startTime = Date.now();
- const delay = 500;
- await sleep(delay);
- const elapsedTime = Date.now() - startTime;
- const margin = 250;
-
- assert.ok(
- elapsedTime >= delay - margin && elapsedTime <= delay + margin,
- `Expected sleep time to be around ${delay}ms (ยฑ${margin}ms), but was ${elapsedTime}ms`
- );
- }),
-
it(async () => {
try {
await waitForPort(NaN, { timeout: 2000 });
@@ -106,12 +94,8 @@ describe('Wait For Port', async () => {
}
}),
]);
-});
-process.on('exit', () => {
- process.on('exit', () => {
- try {
- kill.range(8000, 8003);
- } catch {}
- });
+ try {
+ await kill.range(8000, 8003);
+ } catch {}
});
diff --git a/test/unit/wait-for/sleep.test.ts b/test/unit/wait-for/sleep.test.ts
new file mode 100644
index 00000000..62c4df48
--- /dev/null
+++ b/test/unit/wait-for/sleep.test.ts
@@ -0,0 +1,16 @@
+import { test } from '../../../src/modules/test.js';
+import { assert } from '../../../src/modules/assert.js';
+import { sleep } from '../../../src/modules/wait-for.js';
+
+test('Sleep "mini" helper', async () => {
+ const startTime = Date.now();
+ const delay = 500;
+ await sleep(delay);
+ const elapsedTime = Date.now() - startTime;
+ const margin = 250;
+
+ assert.ok(
+ elapsedTime >= delay - margin && elapsedTime <= delay + margin,
+ `Expected sleep time to be around ${delay}ms (ยฑ${margin}ms), but was ${elapsedTime}ms`
+ );
+});
diff --git a/test/unit/wait-for/wait-for-expected-result.test.ts b/test/unit/wait-for/wait-for-expected-result.test.ts
new file mode 100644
index 00000000..e6f041da
--- /dev/null
+++ b/test/unit/wait-for/wait-for-expected-result.test.ts
@@ -0,0 +1,188 @@
+import { test } from '../../../src/modules/test.js';
+import { assert } from '../../../src/modules/assert.js';
+import { waitForExpectedResult } from '../../../src/modules/wait-for.js';
+import { getRuntime } from '../../../src/helpers/get-runtime.js';
+
+test('Wait For Expected Result', async () => {
+ const runtime = getRuntime();
+
+ class SomeClass {}
+ function someFunc() {
+ true;
+ }
+ const SomeClass2 = class {};
+ const someFunc2 = () => true;
+
+ await Promise.all([
+ assert.doesNotReject(
+ () =>
+ waitForExpectedResult(() => Promise.resolve(someFunc), someFunc, {
+ timeout: 100,
+ }),
+ 'Function'
+ ),
+
+ assert.doesNotReject(
+ () =>
+ waitForExpectedResult(() => Promise.resolve(someFunc2), someFunc2, {
+ timeout: 100,
+ }),
+ 'Function (as const)'
+ ),
+
+ assert.doesNotReject(
+ () =>
+ waitForExpectedResult(
+ () => Promise.resolve(() => true),
+ () => true,
+ {
+ timeout: 100,
+ }
+ ),
+ 'Function (anonymous)'
+ ),
+
+ assert.doesNotReject(
+ () =>
+ waitForExpectedResult(() => Symbol(), Symbol(), {
+ timeout: 100,
+ }),
+ 'Symbol'
+ ),
+
+ assert.doesNotReject(
+ () =>
+ waitForExpectedResult(() => Symbol.for('test'), Symbol.for('test'), {
+ timeout: 100,
+ }),
+ 'Symbol.for'
+ ),
+
+ assert.doesNotReject(
+ () =>
+ waitForExpectedResult(() => new Set([1, 2]), new Set([1, 1, 2, 2]), {
+ timeout: 100,
+ }),
+ 'Set'
+ ),
+
+ assert.doesNotReject(
+ () =>
+ waitForExpectedResult(() => new Map([[1, 2]]), new Map([[1, 2]]), {
+ timeout: 100,
+ }),
+ 'Map'
+ ),
+
+ assert.doesNotReject(
+ () =>
+ waitForExpectedResult(() => [false, 5], [false, 5], {
+ timeout: 100,
+ }),
+ 'Array'
+ ),
+
+ assert.doesNotReject(
+ () =>
+ waitForExpectedResult(
+ () => ({ a: true }),
+ { a: true },
+ {
+ timeout: 100,
+ }
+ ),
+ 'Object'
+ ),
+
+ assert.doesNotReject(
+ () =>
+ waitForExpectedResult(() => 'Hi', 'Hi', {
+ timeout: 100,
+ }),
+ 'String'
+ ),
+
+ assert.doesNotReject(
+ () =>
+ waitForExpectedResult(() => true, true, {
+ timeout: 100,
+ }),
+ 'Boolean (true)'
+ ),
+
+ assert.doesNotReject(
+ () =>
+ waitForExpectedResult(() => false, false, {
+ timeout: 100,
+ }),
+ 'Boolean (false)'
+ ),
+
+ assert.doesNotReject(
+ () =>
+ waitForExpectedResult(() => 3, 3, {
+ timeout: 100,
+ }),
+ 'Number'
+ ),
+
+ assert.doesNotReject(
+ () =>
+ waitForExpectedResult(() => {}, undefined, {
+ timeout: 100,
+ }),
+ 'Undefined (Implict)'
+ ),
+
+ assert.doesNotReject(
+ () =>
+ waitForExpectedResult(() => undefined, undefined, {
+ timeout: 100,
+ }),
+ 'Undefined (Explict)'
+ ),
+
+ assert.doesNotReject(
+ () =>
+ waitForExpectedResult(() => new Error(''), new Error(''), {
+ timeout: 100,
+ }),
+ 'Error'
+ ),
+
+ assert.doesNotReject(
+ () =>
+ waitForExpectedResult(() => SomeClass, SomeClass, {
+ timeout: 100,
+ }),
+ 'Class'
+ ),
+
+ assert.doesNotReject(
+ () =>
+ waitForExpectedResult(() => SomeClass2, SomeClass2, {
+ timeout: 100,
+ }),
+ 'Class (as const)'
+ ),
+
+ runtime === 'node' &&
+ assert.doesNotReject(
+ () =>
+ waitForExpectedResult(() => 1, true, {
+ timeout: 100,
+ strict: false,
+ }),
+ 'No Strict Comparison'
+ ),
+
+ assert.rejects(
+ () =>
+ waitForExpectedResult(() => 1, true, {
+ timeout: 100,
+ strict: true,
+ }),
+ 'Strict Comparison'
+ ),
+ ]);
+});
diff --git a/website/docs/comparing.mdx b/website/docs/comparing.mdx
index f646b62d..5c17bf1a 100644
--- a/website/docs/comparing.mdx
+++ b/website/docs/comparing.mdx
@@ -1,3 +1,7 @@
+---
+tags: [Jest, Mocha, Chai, Vitest, AVA, TypeScript]
+---
+
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
diff --git a/website/docs/documentation/assert/index.mdx b/website/docs/documentation/assert/index.mdx
index 648cee4d..6901520e 100644
--- a/website/docs/documentation/assert/index.mdx
+++ b/website/docs/documentation/assert/index.mdx
@@ -1,5 +1,6 @@
---
sidebar_position: 2
+tags: [assertions]
---
import Tabs from '@theme/Tabs';
diff --git a/website/docs/documentation/helpers/before-after-each/in-code.mdx b/website/docs/documentation/helpers/before-after-each/in-code.mdx
index d97bf280..1968e790 100644
--- a/website/docs/documentation/helpers/before-after-each/in-code.mdx
+++ b/website/docs/documentation/helpers/before-after-each/in-code.mdx
@@ -1,5 +1,6 @@
---
sidebar_position: 2
+tags: [hooks, setup, teardown]
---
import Tabs from '@theme/Tabs';
diff --git a/website/docs/documentation/helpers/before-after-each/per-file.mdx b/website/docs/documentation/helpers/before-after-each/per-file.mdx
index 3c4fe5ef..fe1f565d 100644
--- a/website/docs/documentation/helpers/before-after-each/per-file.mdx
+++ b/website/docs/documentation/helpers/before-after-each/per-file.mdx
@@ -1,5 +1,6 @@
---
sidebar_position: 1
+tags: [hooks, setup, teardown]
---
import Tabs from '@theme/Tabs';
diff --git a/website/docs/documentation/helpers/containers.mdx b/website/docs/documentation/helpers/containers.mdx
index 9f35f016..f1d7a7f5 100644
--- a/website/docs/documentation/helpers/containers.mdx
+++ b/website/docs/documentation/helpers/containers.mdx
@@ -1,5 +1,6 @@
---
sidebar_position: 5
+tags: [containers]
---
import { FAQ } from '@site/src/components/FAQ';
@@ -250,8 +251,10 @@ const result = await poku('./test/integration', {
noExit: true,
});
+// Stops the container
await compose.down();
+// Shows the test results and ends the process with the test exit code.
exit(result);
```
diff --git a/website/docs/documentation/helpers/describe.mdx b/website/docs/documentation/helpers/describe.mdx
index f10490fb..8e04a197 100644
--- a/website/docs/documentation/helpers/describe.mdx
+++ b/website/docs/documentation/helpers/describe.mdx
@@ -1,5 +1,6 @@
---
sidebar_position: 2
+tags: [boilerplate, tdd, bdd]
---
import { FAQ } from '@site/src/components/FAQ';
diff --git a/website/docs/documentation/helpers/it.mdx b/website/docs/documentation/helpers/it.mdx
index 3e817804..238de261 100644
--- a/website/docs/documentation/helpers/it.mdx
+++ b/website/docs/documentation/helpers/it.mdx
@@ -1,5 +1,6 @@
---
sidebar_position: 3
+tags: [boilerplate, tdd, bdd]
---
# ๐งช it
diff --git a/website/docs/documentation/helpers/processes/get-pids.mdx b/website/docs/documentation/helpers/processes/get-pids.mdx
index 2f0fb845..de8ed63e 100644
--- a/website/docs/documentation/helpers/processes/get-pids.mdx
+++ b/website/docs/documentation/helpers/processes/get-pids.mdx
@@ -1,5 +1,5 @@
---
-sidebar_position: 3
+tags: [processes, ports, debug]
---
# Seaching PIDs
diff --git a/website/docs/documentation/helpers/processes/kill.mdx b/website/docs/documentation/helpers/processes/kill.mdx
index 9e4dbbcc..90f86728 100644
--- a/website/docs/documentation/helpers/processes/kill.mdx
+++ b/website/docs/documentation/helpers/processes/kill.mdx
@@ -1,5 +1,6 @@
---
sidebar_position: 1
+tags: [processes, ports]
---
# Killing Processes
diff --git a/website/docs/documentation/helpers/processes/wait-for-expected-result.mdx b/website/docs/documentation/helpers/processes/wait-for-expected-result.mdx
new file mode 100644
index 00000000..b0fd306e
--- /dev/null
+++ b/website/docs/documentation/helpers/processes/wait-for-expected-result.mdx
@@ -0,0 +1,144 @@
+---
+sidebar_position: 3
+tags: [flakey, containers]
+---
+
+import { FAQ } from '@site/src/components/FAQ';
+
+# Waiting For Expected Results
+
+Similar to `assert`, but instead of returning an error when comparing, it will retry until success or timeout.
+
+> Wait for connections, external services to be ready, or a specific result from a method before starting the tests.
+
+## waitForExpectedResult
+
+```ts
+import { waitForExpectedResult } from 'poku';
+
+await waitForExpectedResult(() => true, true, {
+ delay: 0,
+ interval: 100,
+ timeout: 60000,
+ strict: false,
+});
+```
+
+
+
+
+
+```ts
+export type WaitForExpectedResultOptions = {
+ /**
+ * Retry interval in milliseconds
+ *
+ * ---
+ *
+ * @default 100
+ */
+ interval?: number;
+ /**
+ * Timeout in milliseconds
+ *
+ * ---
+ *
+ * @default 60000
+ */
+ timeout?: number;
+ /**
+ * Delays both the start and end by the defined milliseconds.
+ *
+ * ---
+ *
+ * @default 0
+ */
+ delay?: number;
+ /**
+ * Ensure strict comparisons.
+ *
+ * - For **Bun** users, this option isn't necessary.
+ *
+ * ---
+ *
+ * @default false
+ */
+ strict?: boolean;
+};
+```
+
+
+
+
+
+## Examples
+
+Waiting for a Database Connection Returning `true`:
+
+```ts
+import { waitForExpectedResult } from 'poku';
+import { db } from './db.js';
+
+await waitForExpectedResult(() => db.connect(), true);
+// await waitForExpectedResult(async () => await db.connect(), true);
+```
+
+
+
+Waiting for a Database Connection doesn't Throw:
+
+```ts
+import { waitForExpectedResult } from 'poku';
+import { db } from './db.js';
+
+await waitForExpectedResult(async () => {
+ try {
+ await db.connect();
+ return true;
+ } catch {}
+}, true);
+```
+
+
+
+Waiting for a database connection from a container before to run the entire test suite and stops it on finishing:
+
+> **Poku** **API _(in-code)_** usage.
+
+```ts
+import { poku, docker, waitForExpectedResult, exit } from 'poku';
+import { db } from './db.js';
+
+// Load the docker-compose.yml
+const compose = docker.compose();
+
+// Starts the container
+await compose.up();
+
+// Waits for the database
+await waitForExpectedResult(async () => {
+ try {
+ await db.connect();
+ return true;
+ } catch {}
+}, true);
+
+// Starts the test suite
+const result = await poku('./test/integration', {
+ noExit: true,
+});
+
+// Stops the container
+await compose.down();
+
+// Shows the test results and ends the process with the test exit code.
+exit(result);
+```
+
+```sh
+node run.test.mjs
+```
+
+:::tip
+[See an example using **Dockerfile**.](/docs/documentation/helpers/containers#dockerfile)
+:::
diff --git a/website/docs/documentation/helpers/processes/wait-for-port.mdx b/website/docs/documentation/helpers/processes/wait-for-port.mdx
index baaad35b..473ae1b8 100644
--- a/website/docs/documentation/helpers/processes/wait-for-port.mdx
+++ b/website/docs/documentation/helpers/processes/wait-for-port.mdx
@@ -1,5 +1,6 @@
---
sidebar_position: 2
+tags: [flakey, containers]
---
import { FAQ } from '@site/src/components/FAQ';
diff --git a/website/docs/documentation/helpers/startScript.mdx b/website/docs/documentation/helpers/startScript.mdx
index 076c4cf3..20491edf 100644
--- a/website/docs/documentation/helpers/startScript.mdx
+++ b/website/docs/documentation/helpers/startScript.mdx
@@ -1,5 +1,6 @@
---
sidebar_position: 6
+tags: [background, server, service, package.json, scripts]
---
import Tabs from '@theme/Tabs';
diff --git a/website/docs/documentation/helpers/startService.mdx b/website/docs/documentation/helpers/startService.mdx
index a0cfda29..8ecda9dc 100644
--- a/website/docs/documentation/helpers/startService.mdx
+++ b/website/docs/documentation/helpers/startService.mdx
@@ -1,5 +1,6 @@
---
sidebar_position: 7
+tags: [background, server, service]
---
import Tabs from '@theme/Tabs';
diff --git a/website/docs/documentation/helpers/test.mdx b/website/docs/documentation/helpers/test.mdx
index f76f860f..72d4d12e 100644
--- a/website/docs/documentation/helpers/test.mdx
+++ b/website/docs/documentation/helpers/test.mdx
@@ -1,5 +1,6 @@
---
sidebar_position: 1
+tags: [boilerplate, tdd, bdd]
---
# ๐งช test
diff --git a/website/docs/index.mdx b/website/docs/index.mdx
index c1d3e6e1..7d2f8f8a 100644
--- a/website/docs/index.mdx
+++ b/website/docs/index.mdx
@@ -58,7 +58,7 @@ Enjoying **Poku**? [Consider giving him a star](https://github.com/wellwelwel/po
-## Why does Poku Exist?
+## Why does Poku exist?
**Poku** can show you how simple testing can be ๐ฑ
@@ -69,9 +69,7 @@ Enjoying **Poku**? [Consider giving him a star](https://github.com/wellwelwel/po
-
- Run the same test suite for **Node.js** _(6+)_, **Bun** and **Deno**
-
+ Run the same test suite for **Node.js**, **Bun** and **Deno**
@@ -95,7 +93,7 @@ Enjoying **Poku**? [Consider giving him a star](https://github.com/wellwelwel/po
-### Recommended Roadmap
+### Recommended Roadmap ๐ง๐ปโ๐
> ๐ฌ Start by seeing how to use [assert](/docs/documentation/assert).
> ๐งช Then learn how to use [poku](/docs/category/-poku) to run all your test files at once.
@@ -214,6 +212,7 @@ deno run npm:poku
- [**startService**](/docs/documentation/helpers/startService) _(run files in background)_
- [**kill**](/docs/documentation/helpers/processes/kill) _(terminate ports, port ranges, and PIDs)_
- [**waitForPort**](/docs/documentation/helpers/processes/wait-for-port) _(wait for specified ports to become active)_
+- [**waitForExpectedResult**](/docs/documentation/helpers/processes/wait-for-expected-result)_(retry until an expected result or times out)_
- [**getPIDs**](/docs/documentation/helpers/processes/get-pids) _(debug processes IDs using ports and port ranges)_
- _and much more_ ๐๐ป