From bf2119dae5fc6d965de4b43a17e4c8ec92da5bf3 Mon Sep 17 00:00:00 2001 From: Q <264902+kofi-q@users.noreply.github.com> Date: Tue, 17 Jun 2025 20:42:50 -0500 Subject: [PATCH] [misc] Doc updates + add Reader test coverage - Fix docs build race condition where typedoc wipes the Zig documentation if it runs second. - Expanded typedoc config - add base URL, and related links - Add NPM version badge to readme --- README.md | 2 +- build.zig | 1 + lib/client.test.js | 66 +++++++++++++++++++++++++++++++++++++++++----- typedoc.json | 44 ++++++++++++++++++++++++++----- website/styles.css | 12 +++++++++ 5 files changed, 111 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 85c5a05..497b5cc 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# pcsc-mini +# pcsc-mini • NPM Version ` › NodeJS PC/SC API bindings for smart card access on Linux / MacOS / Win32 ` diff --git a/build.zig b/build.zig index 42fe353..83ff5be 100644 --- a/build.zig +++ b/build.zig @@ -163,6 +163,7 @@ fn addDocs( "--includeVersion", "lib", }); docs_ts.step.dependOn(steps.deps); + docs_zig.step.dependOn(&docs_ts.step); steps.docs.dependOn(&docs_ts.step); steps.docs.dependOn(&docs_zig.step); diff --git a/lib/client.test.js b/lib/client.test.js index af7760d..d5d5d96 100644 --- a/lib/client.test.js +++ b/lib/client.test.js @@ -123,6 +123,51 @@ describe("Client", () => { assert.equal(mockOnError.mock.callCount(), 1); }); + test("emits initial reader status event", async () => { + /** @type {pcsc.ReaderChangeHandler | undefined} */ + let onStateChange; + + const mockClient = /** @type {pcsc.Client} */ ({ + start(onChange, _onErr) { + onStateChange = onChange; + }, + }); + + const mockAtr = Uint8Array.of(0xca, 0xfe); + const mockStatusRaw = ReaderStatus.PRESENT | ReaderStatus.IN_USE; + const mockOnChange = mock.fn( + /** + * @param {ReaderStatusFlags} status + * @param {Uint8Array} atr + */ + (status, atr) => { + assert.deepEqual(status, new ReaderStatusFlags(mockStatusRaw)); + assert.strictEqual(atr, mockAtr); + }, + ); + + const client = new Client( + /** @type {pcsc} */ ({ newClient: () => mockClient }), + ); + + /** @type {Reader | undefined} */ + let reader; + + client.on("reader", r => { + reader = r; + reader.on("change", mockOnChange); + }); + + client.start(); + assert(onStateChange); + assert.equal(reader, undefined); + + onStateChange("iReadCards", mockStatusRaw, mockAtr); + assert(reader); + + assert.equal(mockOnChange.mock.callCount(), 1); + }); + test("emits reader status events", async () => { /** @type {pcsc.ReaderChangeHandler | undefined} */ let onStateChange; @@ -134,9 +179,7 @@ describe("Client", () => { }); const client = new Client( - /** @type {pcsc} */ ({ - newClient: () => mockClient, - }), + /** @type {pcsc} */ ({ newClient: () => mockClient }), ); /** @type {Reader | undefined} */ @@ -145,22 +188,31 @@ describe("Client", () => { client.on("reader", r => (reader = r)).start(); assert(onStateChange); + // Set up initial detection state change: onStateChange("iReadCards", ReaderStatus.EMPTY, Uint8Array.of()); assert(reader); + const mockAtr = Uint8Array.of(0xca, 0xfe); const mockStatusRaw = ReaderStatus.PRESENT | ReaderStatus.IN_USE; const mockOnChange = mock.fn( - /** @param {ReaderStatusFlags} status */ - status => assert.deepEqual(status, new ReaderStatusFlags(mockStatusRaw)), + /** + * @param {ReaderStatusFlags} status + * @param {Uint8Array} atr + */ + (status, atr) => { + assert.deepEqual(status, new ReaderStatusFlags(mockStatusRaw)); + assert.strictEqual(atr, mockAtr); + }, ); reader.on("change", mockOnChange); assert.equal(mockOnChange.mock.callCount(), 0); - onStateChange("other reader", mockStatusRaw, Uint8Array.of()); + client.removeAllListeners("reader"); + onStateChange("other reader", mockStatusRaw, mockAtr); assert.equal(mockOnChange.mock.callCount(), 0); - onStateChange("iReadCards", mockStatusRaw, Uint8Array.of()); + onStateChange("iReadCards", mockStatusRaw, mockAtr); assert.equal(mockOnChange.mock.callCount(), 1); }); diff --git a/typedoc.json b/typedoc.json index 7e63cb1..95bfa39 100644 --- a/typedoc.json +++ b/typedoc.json @@ -1,11 +1,43 @@ { + "$schema": "https://typedoc.org/schema.json", "headings": { - "readme": false + "readme": false }, + "hostedBaseUrl": "https://kofi-q.github.io/pcsc-mini", + "kindSortOrder": [ + "Class", + "Enum", + "Interface", + "Reference", + "Project", + "Module", + "Namespace", + "EnumMember", + "TypeAlias", + "Constructor", + "Property", + "Variable", + "Function", + "Accessor", + "Method", + "Parameter", + "TypeParameter", + "TypeLiteral", + "CallSignature", + "ConstructorSignature", + "IndexSignature", + "GetSignature", + "SetSignature" + ], "outputs": [ - { - "name": "html", - "path": "./docs" - } - ] + { + "name": "html", + "path": "./docs" + } + ], + "navigationLinks": { + "Zig Docs": "https://kofi-q.github.io/pcsc-mini/zig", + "NPM": "https://npmjs.com/package/pcsc-mini", + "GitHub": "https://github.com/kofi-q/pcsc-mini" + }, } diff --git a/website/styles.css b/website/styles.css index 475247c..782feaf 100644 --- a/website/styles.css +++ b/website/styles.css @@ -102,6 +102,18 @@ code.tsd-tag { } } +.tsd-accordion-details { + >a { + /* + * A little hacky, but typedoc pulls in the NPM badge logo in the readme + * title into the sidebar as escaped HTML. + * [TODO] Figure out a way around this, or do it in JS instead to avoid + * hiding on all pages. + */ + display: none; + } +} + ul.tsd-parameter-list { li { span {