@zonuexe/techbook-mcp 0.2.3 → 0.3.1
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.
- package/CHANGELOG.md +52 -1
- package/README.md +54 -22
- package/dist/adapters/http/fetch-client.d.ts.map +1 -1
- package/dist/adapters/http/fetch-client.js +18 -1
- package/dist/adapters/http/fetch-client.js.map +1 -1
- package/dist/adapters/openbd.d.ts.map +1 -1
- package/dist/adapters/openbd.js +18 -5
- package/dist/adapters/openbd.js.map +1 -1
- package/dist/adapters/publishers/base.d.ts +2 -1
- package/dist/adapters/publishers/base.d.ts.map +1 -1
- package/dist/adapters/publishers/base.js +8 -3
- package/dist/adapters/publishers/base.js.map +1 -1
- package/dist/adapters/publishers/cq-publishing.d.ts +3 -0
- package/dist/adapters/publishers/cq-publishing.d.ts.map +1 -0
- package/dist/adapters/publishers/cq-publishing.js +120 -0
- package/dist/adapters/publishers/cq-publishing.js.map +1 -0
- package/dist/adapters/publishers/google-books.d.ts +4 -0
- package/dist/adapters/publishers/google-books.d.ts.map +1 -0
- package/dist/adapters/publishers/google-books.js +76 -0
- package/dist/adapters/publishers/google-books.js.map +1 -0
- package/dist/adapters/publishers/isbn-publisher-codes.d.ts +21 -0
- package/dist/adapters/publishers/isbn-publisher-codes.d.ts.map +1 -0
- package/dist/adapters/publishers/isbn-publisher-codes.js +49 -0
- package/dist/adapters/publishers/isbn-publisher-codes.js.map +1 -0
- package/dist/adapters/publishers/juse-p.d.ts +3 -0
- package/dist/adapters/publishers/juse-p.d.ts.map +1 -0
- package/dist/adapters/publishers/juse-p.js +110 -0
- package/dist/adapters/publishers/juse-p.js.map +1 -0
- package/dist/adapters/publishers/leanpub.d.ts +3 -0
- package/dist/adapters/publishers/leanpub.d.ts.map +1 -0
- package/dist/adapters/publishers/leanpub.js +96 -0
- package/dist/adapters/publishers/leanpub.js.map +1 -0
- package/dist/adapters/publishers/oreilly-japan.d.ts.map +1 -1
- package/dist/adapters/publishers/oreilly-japan.js +8 -2
- package/dist/adapters/publishers/oreilly-japan.js.map +1 -1
- package/dist/adapters/publishers/peaks.d.ts.map +1 -1
- package/dist/adapters/publishers/peaks.js +3 -2
- package/dist/adapters/publishers/peaks.js.map +1 -1
- package/dist/adapters/publishers/personal-media.d.ts.map +1 -1
- package/dist/adapters/publishers/personal-media.js +3 -2
- package/dist/adapters/publishers/personal-media.js.map +1 -1
- package/dist/adapters/publishers/pragprog.d.ts +3 -0
- package/dist/adapters/publishers/pragprog.d.ts.map +1 -0
- package/dist/adapters/publishers/pragprog.js +120 -0
- package/dist/adapters/publishers/pragprog.js.map +1 -0
- package/dist/adapters/publishers/registry.d.ts.map +1 -1
- package/dist/adapters/publishers/registry.js +10 -0
- package/dist/adapters/publishers/registry.js.map +1 -1
- package/dist/adapters/publishers/techbookfest.d.ts.map +1 -1
- package/dist/adapters/publishers/techbookfest.js +2 -1
- package/dist/adapters/publishers/techbookfest.js.map +1 -1
- package/dist/application/concurrency.d.ts +16 -0
- package/dist/application/concurrency.d.ts.map +1 -0
- package/dist/application/concurrency.js +42 -0
- package/dist/application/concurrency.js.map +1 -0
- package/dist/application/get-book-by-isbn.d.ts +0 -8
- package/dist/application/get-book-by-isbn.d.ts.map +1 -1
- package/dist/application/get-book-by-isbn.js +64 -7
- package/dist/application/get-book-by-isbn.js.map +1 -1
- package/dist/application/get-book-detail.d.ts.map +1 -1
- package/dist/application/get-book-detail.js +3 -0
- package/dist/application/get-book-detail.js.map +1 -1
- package/dist/application/search-books.d.ts +16 -5
- package/dist/application/search-books.d.ts.map +1 -1
- package/dist/application/search-books.js +46 -9
- package/dist/application/search-books.js.map +1 -1
- package/dist/config/credentials.d.ts +8 -0
- package/dist/config/credentials.d.ts.map +1 -0
- package/dist/config/credentials.js +32 -0
- package/dist/config/credentials.js.map +1 -0
- package/dist/domain/authors.d.ts +7 -0
- package/dist/domain/authors.d.ts.map +1 -0
- package/dist/domain/authors.js +22 -0
- package/dist/domain/authors.js.map +1 -0
- package/dist/domain/book.d.ts +2 -0
- package/dist/domain/book.d.ts.map +1 -1
- package/dist/domain/isbn.d.ts +8 -0
- package/dist/domain/isbn.d.ts.map +1 -0
- package/dist/domain/isbn.js +16 -0
- package/dist/domain/isbn.js.map +1 -0
- package/dist/domain/publisher.d.ts +16 -0
- package/dist/domain/publisher.d.ts.map +1 -1
- package/dist/domain/text-match.d.ts +32 -0
- package/dist/domain/text-match.d.ts.map +1 -0
- package/dist/domain/text-match.js +84 -0
- package/dist/domain/text-match.js.map +1 -0
- package/dist/main.js +15 -1
- package/dist/main.js.map +1 -1
- package/dist/mcp/server.d.ts +6 -0
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +40 -4
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +9 -1
- package/dist/mcp/tools.js.map +1 -1
- package/dist/setup.d.ts +2 -0
- package/dist/setup.d.ts.map +1 -0
- package/dist/setup.js +43 -0
- package/dist/setup.js.map +1 -0
- package/dist/version.d.ts +9 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +9 -0
- package/dist/version.js.map +1 -0
- package/docs/design-doc.md +127 -7
- package/package.json +14 -15
- package/.claude/settings.local.json +0 -38
- package/.codex/skills/techbook-mcp-release-prep/SKILL.md +0 -105
- package/.github/workflows/test.yml +0 -72
- package/.oxlintrc.json +0 -12
- package/AGENTS.md +0 -100
- package/deno.json +0 -3
- package/src/adapters/cache/memory-cache.ts +0 -31
- package/src/adapters/cache/null-cache.ts +0 -8
- package/src/adapters/calil.ts +0 -57
- package/src/adapters/html/cheerio-parser.ts +0 -50
- package/src/adapters/http/fetch-client.ts +0 -47
- package/src/adapters/http/mock-client.ts +0 -77
- package/src/adapters/openbd.ts +0 -142
- package/src/adapters/publishers/base.ts +0 -279
- package/src/adapters/publishers/book-tech.ts +0 -117
- package/src/adapters/publishers/born-digital.ts +0 -143
- package/src/adapters/publishers/coronasha.ts +0 -139
- package/src/adapters/publishers/gihyo.ts +0 -120
- package/src/adapters/publishers/impress.ts +0 -103
- package/src/adapters/publishers/lambdanote.ts +0 -146
- package/src/adapters/publishers/manatee.ts +0 -113
- package/src/adapters/publishers/maruzen-publishing.ts +0 -129
- package/src/adapters/publishers/optronics.ts +0 -113
- package/src/adapters/publishers/oreilly-japan.ts +0 -133
- package/src/adapters/publishers/peaks.ts +0 -98
- package/src/adapters/publishers/personal-media.ts +0 -168
- package/src/adapters/publishers/registry.ts +0 -38
- package/src/adapters/publishers/rutles.ts +0 -149
- package/src/adapters/publishers/saiensu.ts +0 -136
- package/src/adapters/publishers/seshop.ts +0 -121
- package/src/adapters/publishers/tatsu-zine.ts +0 -142
- package/src/adapters/publishers/techbookfest.ts +0 -179
- package/src/application/get-book-by-isbn.ts +0 -50
- package/src/application/get-book-detail.ts +0 -40
- package/src/application/search-books.ts +0 -64
- package/src/domain/book.ts +0 -35
- package/src/domain/publisher.ts +0 -18
- package/src/main.ts +0 -14
- package/src/mcp/server.ts +0 -113
- package/src/mcp/tools.ts +0 -71
- package/src/ports/cache.ts +0 -5
- package/src/ports/html-parser.ts +0 -15
- package/src/ports/http.ts +0 -17
- package/tests/fixtures/book-tech-detail.html +0 -51
- package/tests/fixtures/book-tech-search.html +0 -91
- package/tests/fixtures/born-digital-detail.html +0 -62
- package/tests/fixtures/born-digital-search.html +0 -51
- package/tests/fixtures/calil-book.html +0 -987
- package/tests/fixtures/coronasha-detail.html +0 -41
- package/tests/fixtures/coronasha-search.html +0 -61
- package/tests/fixtures/gihyo-detail.html +0 -42
- package/tests/fixtures/gihyo-search.json +0 -54
- package/tests/fixtures/impress-detail-epub.html +0 -746
- package/tests/fixtures/impress-detail-social.html +0 -689
- package/tests/fixtures/lambdanote-search.html +0 -66
- package/tests/fixtures/manatee-detail.html +0 -53
- package/tests/fixtures/manatee-search.html +0 -59
- package/tests/fixtures/maruzen-detail.html +0 -51
- package/tests/fixtures/maruzen-search.html +0 -60
- package/tests/fixtures/openbd-response.json +0 -110
- package/tests/fixtures/optronics-detail.html +0 -30
- package/tests/fixtures/optronics-search.html +0 -75
- package/tests/fixtures/oreilly-detail.html +0 -52
- package/tests/fixtures/oreilly-ebook-list.html +0 -53
- package/tests/fixtures/peaks-detail.html +0 -39
- package/tests/fixtures/peaks-top.html +0 -50
- package/tests/fixtures/personal-media-detail.html +0 -32
- package/tests/fixtures/personal-media-search.html +0 -39
- package/tests/fixtures/rutles-detail.html +0 -32
- package/tests/fixtures/rutles-search.html +0 -62
- package/tests/fixtures/saiensu-detail.html +0 -41
- package/tests/fixtures/saiensu-search.html +0 -65
- package/tests/fixtures/seshop-detail.html +0 -45
- package/tests/fixtures/seshop-search.html +0 -58
- package/tests/fixtures/tatsu-zine-detail-free.html +0 -24
- package/tests/fixtures/tatsu-zine-search.html +0 -40
- package/tests/fixtures/techbookfest-search.json +0 -73
- package/tests/unit/adapters/base.test.ts +0 -441
- package/tests/unit/adapters/calil.test.ts +0 -69
- package/tests/unit/adapters/openbd.test.ts +0 -185
- package/tests/unit/adapters/publishers/book-tech.test.ts +0 -186
- package/tests/unit/adapters/publishers/born-digital.test.ts +0 -194
- package/tests/unit/adapters/publishers/coronasha.test.ts +0 -207
- package/tests/unit/adapters/publishers/gihyo.test.ts +0 -137
- package/tests/unit/adapters/publishers/impress.test.ts +0 -129
- package/tests/unit/adapters/publishers/lambdanote.test.ts +0 -85
- package/tests/unit/adapters/publishers/manatee.test.ts +0 -165
- package/tests/unit/adapters/publishers/maruzen-publishing.test.ts +0 -179
- package/tests/unit/adapters/publishers/optronics.test.ts +0 -208
- package/tests/unit/adapters/publishers/oreilly-japan.test.ts +0 -194
- package/tests/unit/adapters/publishers/peaks.test.ts +0 -177
- package/tests/unit/adapters/publishers/personal-media.test.ts +0 -199
- package/tests/unit/adapters/publishers/rutles.test.ts +0 -173
- package/tests/unit/adapters/publishers/saiensu.test.ts +0 -169
- package/tests/unit/adapters/publishers/seshop.test.ts +0 -174
- package/tests/unit/adapters/publishers/tatsu-zine.test.ts +0 -172
- package/tests/unit/adapters/publishers/techbookfest.test.ts +0 -94
- package/tests/unit/adapters/registry.test.ts +0 -37
- package/tests/unit/application/get-book-by-isbn.test.ts +0 -176
- package/tests/unit/application/get-book-detail.test.ts +0 -102
- package/tests/unit/application/search-books.test.ts +0 -137
- package/tsconfig.json +0 -17
|
@@ -1,186 +0,0 @@
|
|
|
1
|
-
import { describe, it } from "node:test";
|
|
2
|
-
import assert from "node:assert/strict";
|
|
3
|
-
import { readFile } from "node:fs/promises";
|
|
4
|
-
import { join } from "node:path";
|
|
5
|
-
import { bookTechAdapter } from "../../../../src/adapters/publishers/book-tech.js";
|
|
6
|
-
import { MockHttpClient } from "../../../../src/adapters/http/mock-client.js";
|
|
7
|
-
import { CheerioHtmlParser } from "../../../../src/adapters/html/cheerio-parser.js";
|
|
8
|
-
import { NullCacheStore } from "../../../../src/adapters/cache/null-cache.js";
|
|
9
|
-
|
|
10
|
-
const FIXTURES_DIR = join(import.meta.dirname, "../../../fixtures");
|
|
11
|
-
|
|
12
|
-
function makeDeps(http: MockHttpClient) {
|
|
13
|
-
return { http, parser: new CheerioHtmlParser(), cache: new NullCacheStore() };
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
async function loadFixture(name: string): Promise<string> {
|
|
17
|
-
return readFile(join(FIXTURES_DIR, name), "utf-8");
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
describe("bookTechAdapter", () => {
|
|
21
|
-
describe("search()", () => {
|
|
22
|
-
it("BookRecord[] を返す", async () => {
|
|
23
|
-
const body = await loadFixture("book-tech-search.html");
|
|
24
|
-
const http = new MockHttpClient().addResponse(
|
|
25
|
-
"https://book-tech.com/books",
|
|
26
|
-
{ status: 200, body },
|
|
27
|
-
);
|
|
28
|
-
|
|
29
|
-
const results = await bookTechAdapter.search({ title: "TypeScript" }, makeDeps(http));
|
|
30
|
-
|
|
31
|
-
assert.strictEqual(results.length, 2);
|
|
32
|
-
assert.partialDeepStrictEqual(results[0], {
|
|
33
|
-
title: "次のステップへ!React実践開発 サクサク作って学ぶ UI/テスト/デプロイ",
|
|
34
|
-
publisher: "インプレス NextPublishing",
|
|
35
|
-
url: "https://book-tech.com/books/d80ffe3d-f3fe-458b-95ee-b4dd3327fab2",
|
|
36
|
-
price: 2178,
|
|
37
|
-
publishedAt: "2026-02-20",
|
|
38
|
-
});
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it("著者名から役割語を除去する", async () => {
|
|
42
|
-
const body = await loadFixture("book-tech-search.html");
|
|
43
|
-
const http = new MockHttpClient().addResponse(
|
|
44
|
-
"https://book-tech.com/books",
|
|
45
|
-
{ status: 200, body },
|
|
46
|
-
);
|
|
47
|
-
|
|
48
|
-
const results = await bookTechAdapter.search({ title: "TypeScript" }, makeDeps(http));
|
|
49
|
-
|
|
50
|
-
assert.deepStrictEqual(results[0].authors, ["philosophy"]);
|
|
51
|
-
assert.deepStrictEqual(results[1].authors, ["井手 優太"]);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it("coverImageUrl が設定される", async () => {
|
|
55
|
-
const body = await loadFixture("book-tech-search.html");
|
|
56
|
-
const http = new MockHttpClient().addResponse(
|
|
57
|
-
"https://book-tech.com/books",
|
|
58
|
-
{ status: 200, body },
|
|
59
|
-
);
|
|
60
|
-
|
|
61
|
-
const results = await bookTechAdapter.search({ title: "TypeScript" }, makeDeps(http));
|
|
62
|
-
|
|
63
|
-
assert.strictEqual(
|
|
64
|
-
results[0].coverImageUrl,
|
|
65
|
-
"https://booktech-share.s3-ap-northeast-1.amazonaws.com/books/d80ffe3d.webp",
|
|
66
|
-
);
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it("ebookStores に BOOK TECH (social DRM) が含まれる", async () => {
|
|
70
|
-
const body = await loadFixture("book-tech-search.html");
|
|
71
|
-
const http = new MockHttpClient().addResponse(
|
|
72
|
-
"https://book-tech.com/books",
|
|
73
|
-
{ status: 200, body },
|
|
74
|
-
);
|
|
75
|
-
|
|
76
|
-
const results = await bookTechAdapter.search({ title: "TypeScript" }, makeDeps(http));
|
|
77
|
-
|
|
78
|
-
assert.deepStrictEqual(results[0].ebookStores, [
|
|
79
|
-
{
|
|
80
|
-
name: "BOOK TECH",
|
|
81
|
-
url: "https://book-tech.com/books/d80ffe3d-f3fe-458b-95ee-b4dd3327fab2",
|
|
82
|
-
drm: "social",
|
|
83
|
-
},
|
|
84
|
-
]);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
it("title も author も空の場合は [] を返しHTTPを呼ばない", async () => {
|
|
88
|
-
const http = new MockHttpClient();
|
|
89
|
-
|
|
90
|
-
const results = await bookTechAdapter.search({}, makeDeps(http));
|
|
91
|
-
|
|
92
|
-
assert.deepStrictEqual(results, []);
|
|
93
|
-
assert.strictEqual(http.calls.length, 0);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
it("検索リクエストに q[...] パラメータが含まれる", async () => {
|
|
97
|
-
const body = await loadFixture("book-tech-search.html");
|
|
98
|
-
const http = new MockHttpClient().addResponse(
|
|
99
|
-
"https://book-tech.com/books",
|
|
100
|
-
{ status: 200, body },
|
|
101
|
-
);
|
|
102
|
-
|
|
103
|
-
await bookTechAdapter.search({ title: "TypeScript" }, makeDeps(http));
|
|
104
|
-
|
|
105
|
-
assert.ok(http.calls[0].includes("TypeScript"));
|
|
106
|
-
assert.ok(http.calls[0].includes("title_or_overview"));
|
|
107
|
-
});
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
describe("getDetail()", () => {
|
|
111
|
-
it("詳細情報を返す", async () => {
|
|
112
|
-
const body = await loadFixture("book-tech-detail.html");
|
|
113
|
-
const http = new MockHttpClient().addResponse(
|
|
114
|
-
"https://book-tech.com/books/d80ffe3d-f3fe-458b-95ee-b4dd3327fab2",
|
|
115
|
-
{ status: 200, body },
|
|
116
|
-
);
|
|
117
|
-
|
|
118
|
-
const book = await bookTechAdapter.getDetail(
|
|
119
|
-
"https://book-tech.com/books/d80ffe3d-f3fe-458b-95ee-b4dd3327fab2",
|
|
120
|
-
makeDeps(http),
|
|
121
|
-
);
|
|
122
|
-
|
|
123
|
-
assert.partialDeepStrictEqual(book, {
|
|
124
|
-
title: "次のステップへ!React実践開発 サクサク作って学ぶ UI/テスト/デプロイ",
|
|
125
|
-
publisher: "インプレス NextPublishing",
|
|
126
|
-
isbn: "9784295604136",
|
|
127
|
-
price: 2178,
|
|
128
|
-
publishedAt: "2026-02-20",
|
|
129
|
-
});
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
it("著者名から役割語を除去する", async () => {
|
|
133
|
-
const body = await loadFixture("book-tech-detail.html");
|
|
134
|
-
const http = new MockHttpClient().addResponse(
|
|
135
|
-
"https://book-tech.com/books/d80ffe3d-f3fe-458b-95ee-b4dd3327fab2",
|
|
136
|
-
{ status: 200, body },
|
|
137
|
-
);
|
|
138
|
-
|
|
139
|
-
const book = await bookTechAdapter.getDetail(
|
|
140
|
-
"https://book-tech.com/books/d80ffe3d-f3fe-458b-95ee-b4dd3327fab2",
|
|
141
|
-
makeDeps(http),
|
|
142
|
-
);
|
|
143
|
-
|
|
144
|
-
assert.deepStrictEqual(book.authors, ["philosophy"]);
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
it("coverImageUrl が設定される", async () => {
|
|
148
|
-
const body = await loadFixture("book-tech-detail.html");
|
|
149
|
-
const http = new MockHttpClient().addResponse(
|
|
150
|
-
"https://book-tech.com/books/d80ffe3d-f3fe-458b-95ee-b4dd3327fab2",
|
|
151
|
-
{ status: 200, body },
|
|
152
|
-
);
|
|
153
|
-
|
|
154
|
-
const book = await bookTechAdapter.getDetail(
|
|
155
|
-
"https://book-tech.com/books/d80ffe3d-f3fe-458b-95ee-b4dd3327fab2",
|
|
156
|
-
makeDeps(http),
|
|
157
|
-
);
|
|
158
|
-
|
|
159
|
-
assert.strictEqual(
|
|
160
|
-
book.coverImageUrl,
|
|
161
|
-
"https://booktech-share.s3-ap-northeast-1.amazonaws.com/books/d80ffe3d.webp",
|
|
162
|
-
);
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
it("ebookStores に BOOK TECH (social DRM) が含まれる", async () => {
|
|
166
|
-
const body = await loadFixture("book-tech-detail.html");
|
|
167
|
-
const http = new MockHttpClient().addResponse(
|
|
168
|
-
"https://book-tech.com/books/d80ffe3d-f3fe-458b-95ee-b4dd3327fab2",
|
|
169
|
-
{ status: 200, body },
|
|
170
|
-
);
|
|
171
|
-
|
|
172
|
-
const book = await bookTechAdapter.getDetail(
|
|
173
|
-
"https://book-tech.com/books/d80ffe3d-f3fe-458b-95ee-b4dd3327fab2",
|
|
174
|
-
makeDeps(http),
|
|
175
|
-
);
|
|
176
|
-
|
|
177
|
-
assert.deepStrictEqual(book.ebookStores, [
|
|
178
|
-
{
|
|
179
|
-
name: "BOOK TECH",
|
|
180
|
-
url: "https://book-tech.com/books/d80ffe3d-f3fe-458b-95ee-b4dd3327fab2",
|
|
181
|
-
drm: "social",
|
|
182
|
-
},
|
|
183
|
-
]);
|
|
184
|
-
});
|
|
185
|
-
});
|
|
186
|
-
});
|
|
@@ -1,194 +0,0 @@
|
|
|
1
|
-
import { describe, it } from "node:test";
|
|
2
|
-
import assert from "node:assert/strict";
|
|
3
|
-
import { readFile } from "node:fs/promises";
|
|
4
|
-
import { join } from "node:path";
|
|
5
|
-
import { bornDigitalAdapter } from "../../../../src/adapters/publishers/born-digital.js";
|
|
6
|
-
import { MockHttpClient } from "../../../../src/adapters/http/mock-client.js";
|
|
7
|
-
import { CheerioHtmlParser } from "../../../../src/adapters/html/cheerio-parser.js";
|
|
8
|
-
import { NullCacheStore } from "../../../../src/adapters/cache/null-cache.js";
|
|
9
|
-
|
|
10
|
-
const FIXTURES_DIR = join(import.meta.dirname, "../../../fixtures");
|
|
11
|
-
|
|
12
|
-
function makeDeps(http: MockHttpClient) {
|
|
13
|
-
return { http, parser: new CheerioHtmlParser(), cache: new NullCacheStore() };
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
async function loadFixture(name: string): Promise<string> {
|
|
17
|
-
return readFile(join(FIXTURES_DIR, name), "utf-8");
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
describe("bornDigitalAdapter", () => {
|
|
21
|
-
describe("search()", () => {
|
|
22
|
-
it("電子書籍のみ BookRecord[] を返す(紙書籍は除外)", async () => {
|
|
23
|
-
const body = await loadFixture("born-digital-search.html");
|
|
24
|
-
const http = new MockHttpClient().addResponse(
|
|
25
|
-
"https://wgn-obs.shop-pro.jp/",
|
|
26
|
-
{ status: 200, body },
|
|
27
|
-
);
|
|
28
|
-
|
|
29
|
-
const results = await bornDigitalAdapter.search({ title: "HTML" }, makeDeps(http));
|
|
30
|
-
|
|
31
|
-
// フィクスチャには電子2件・紙1件あり、電子のみ返す
|
|
32
|
-
assert.strictEqual(results.length, 2);
|
|
33
|
-
assert.partialDeepStrictEqual(results[0], {
|
|
34
|
-
title: "【PDFダウンロード版】HTML解体新書 ー仕様から紐解く本格入門",
|
|
35
|
-
publisher: "ボーンデジタル",
|
|
36
|
-
url: "https://wgn-obs.shop-pro.jp/?pid=167400957",
|
|
37
|
-
price: 3520,
|
|
38
|
-
});
|
|
39
|
-
assert.partialDeepStrictEqual(results[1], {
|
|
40
|
-
title: "【電子書籍版】インクルーシブHTML+CSS & JavaScript",
|
|
41
|
-
publisher: "ボーンデジタル",
|
|
42
|
-
url: "https://wgn-obs.shop-pro.jp/?pid=144269584",
|
|
43
|
-
price: 2640,
|
|
44
|
-
});
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it("coverImageUrl が設定される", async () => {
|
|
48
|
-
const body = await loadFixture("born-digital-search.html");
|
|
49
|
-
const http = new MockHttpClient().addResponse(
|
|
50
|
-
"https://wgn-obs.shop-pro.jp/",
|
|
51
|
-
{ status: 200, body },
|
|
52
|
-
);
|
|
53
|
-
|
|
54
|
-
const results = await bornDigitalAdapter.search({ title: "HTML" }, makeDeps(http));
|
|
55
|
-
|
|
56
|
-
assert.strictEqual(
|
|
57
|
-
results[0].coverImageUrl,
|
|
58
|
-
"https://img07.shop-pro.jp/PA01427/945/product/167400957_th.png?cmsp_timestamp=20220328140327",
|
|
59
|
-
);
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it("ebookStores に ボーンデジタル (social DRM) が含まれる", async () => {
|
|
63
|
-
const body = await loadFixture("born-digital-search.html");
|
|
64
|
-
const http = new MockHttpClient().addResponse(
|
|
65
|
-
"https://wgn-obs.shop-pro.jp/",
|
|
66
|
-
{ status: 200, body },
|
|
67
|
-
);
|
|
68
|
-
|
|
69
|
-
const results = await bornDigitalAdapter.search({ title: "HTML" }, makeDeps(http));
|
|
70
|
-
|
|
71
|
-
assert.deepStrictEqual(results[0].ebookStores, [
|
|
72
|
-
{
|
|
73
|
-
name: "ボーンデジタル",
|
|
74
|
-
url: "https://wgn-obs.shop-pro.jp/?pid=167400957",
|
|
75
|
-
drm: "social",
|
|
76
|
-
},
|
|
77
|
-
]);
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
it("title も author も空の場合は [] を返しHTTPを呼ばない", async () => {
|
|
81
|
-
const http = new MockHttpClient();
|
|
82
|
-
|
|
83
|
-
const results = await bornDigitalAdapter.search({}, makeDeps(http));
|
|
84
|
-
|
|
85
|
-
assert.deepStrictEqual(results, []);
|
|
86
|
-
assert.strictEqual(http.calls.length, 0);
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
it("検索リクエストに EUC-JP エンコードされた keyword が含まれる", async () => {
|
|
90
|
-
const body = await loadFixture("born-digital-search.html");
|
|
91
|
-
const http = new MockHttpClient().addResponse(
|
|
92
|
-
"https://wgn-obs.shop-pro.jp/",
|
|
93
|
-
{ status: 200, body },
|
|
94
|
-
);
|
|
95
|
-
|
|
96
|
-
await bornDigitalAdapter.search({ title: "HTML" }, makeDeps(http));
|
|
97
|
-
|
|
98
|
-
assert.ok(http.calls[0].includes("mode=srh"));
|
|
99
|
-
// ASCII は EUC-JP でも同じバイト列だがパーセントエンコードされる
|
|
100
|
-
assert.ok(http.calls[0].includes("keyword=%48%54%4D%4C"));
|
|
101
|
-
});
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
describe("getDetail()", () => {
|
|
105
|
-
it("詳細情報を返す", async () => {
|
|
106
|
-
const body = await loadFixture("born-digital-detail.html");
|
|
107
|
-
const http = new MockHttpClient().addResponse(
|
|
108
|
-
"https://wgn-obs.shop-pro.jp/?pid=167400957",
|
|
109
|
-
{ status: 200, body },
|
|
110
|
-
);
|
|
111
|
-
|
|
112
|
-
const book = await bornDigitalAdapter.getDetail(
|
|
113
|
-
"https://wgn-obs.shop-pro.jp/?pid=167400957",
|
|
114
|
-
makeDeps(http),
|
|
115
|
-
);
|
|
116
|
-
|
|
117
|
-
assert.partialDeepStrictEqual(book, {
|
|
118
|
-
title: "【PDFダウンロード版】HTML解体新書 ー仕様から紐解く本格入門",
|
|
119
|
-
publisher: "ボーンデジタル",
|
|
120
|
-
price: 3520,
|
|
121
|
-
publishedAt: "2022-04-07",
|
|
122
|
-
});
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
it("著者を取得する", async () => {
|
|
126
|
-
const body = await loadFixture("born-digital-detail.html");
|
|
127
|
-
const http = new MockHttpClient().addResponse(
|
|
128
|
-
"https://wgn-obs.shop-pro.jp/?pid=167400957",
|
|
129
|
-
{ status: 200, body },
|
|
130
|
-
);
|
|
131
|
-
|
|
132
|
-
const book = await bornDigitalAdapter.getDetail(
|
|
133
|
-
"https://wgn-obs.shop-pro.jp/?pid=167400957",
|
|
134
|
-
makeDeps(http),
|
|
135
|
-
);
|
|
136
|
-
|
|
137
|
-
assert.deepStrictEqual(book.authors, ["太田 良典", "中村 直樹"]);
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
it("Colorme JSON から価格を取得する", async () => {
|
|
141
|
-
const body = await loadFixture("born-digital-detail.html");
|
|
142
|
-
const http = new MockHttpClient().addResponse(
|
|
143
|
-
"https://wgn-obs.shop-pro.jp/?pid=167400957",
|
|
144
|
-
{ status: 200, body },
|
|
145
|
-
);
|
|
146
|
-
|
|
147
|
-
const book = await bornDigitalAdapter.getDetail(
|
|
148
|
-
"https://wgn-obs.shop-pro.jp/?pid=167400957",
|
|
149
|
-
makeDeps(http),
|
|
150
|
-
);
|
|
151
|
-
|
|
152
|
-
assert.strictEqual(book.price, 3520);
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
it("coverImageUrl が設定される", async () => {
|
|
156
|
-
const body = await loadFixture("born-digital-detail.html");
|
|
157
|
-
const http = new MockHttpClient().addResponse(
|
|
158
|
-
"https://wgn-obs.shop-pro.jp/?pid=167400957",
|
|
159
|
-
{ status: 200, body },
|
|
160
|
-
);
|
|
161
|
-
|
|
162
|
-
const book = await bornDigitalAdapter.getDetail(
|
|
163
|
-
"https://wgn-obs.shop-pro.jp/?pid=167400957",
|
|
164
|
-
makeDeps(http),
|
|
165
|
-
);
|
|
166
|
-
|
|
167
|
-
assert.strictEqual(
|
|
168
|
-
book.coverImageUrl,
|
|
169
|
-
"https://img07.shop-pro.jp/PA01427/945/product/167400957.png?cmsp_timestamp=20220328140327",
|
|
170
|
-
);
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
it("ebookStores に ボーンデジタル (social DRM) が含まれる", async () => {
|
|
174
|
-
const body = await loadFixture("born-digital-detail.html");
|
|
175
|
-
const http = new MockHttpClient().addResponse(
|
|
176
|
-
"https://wgn-obs.shop-pro.jp/?pid=167400957",
|
|
177
|
-
{ status: 200, body },
|
|
178
|
-
);
|
|
179
|
-
|
|
180
|
-
const book = await bornDigitalAdapter.getDetail(
|
|
181
|
-
"https://wgn-obs.shop-pro.jp/?pid=167400957",
|
|
182
|
-
makeDeps(http),
|
|
183
|
-
);
|
|
184
|
-
|
|
185
|
-
assert.deepStrictEqual(book.ebookStores, [
|
|
186
|
-
{
|
|
187
|
-
name: "ボーンデジタル",
|
|
188
|
-
url: "https://wgn-obs.shop-pro.jp/?pid=167400957",
|
|
189
|
-
drm: "social",
|
|
190
|
-
},
|
|
191
|
-
]);
|
|
192
|
-
});
|
|
193
|
-
});
|
|
194
|
-
});
|
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
import { describe, it } from "node:test";
|
|
2
|
-
import assert from "node:assert/strict";
|
|
3
|
-
import { readFile } from "node:fs/promises";
|
|
4
|
-
import { join } from "node:path";
|
|
5
|
-
import { coronashaAdapter } from "../../../../src/adapters/publishers/coronasha.js";
|
|
6
|
-
import { MockHttpClient } from "../../../../src/adapters/http/mock-client.js";
|
|
7
|
-
import { CheerioHtmlParser } from "../../../../src/adapters/html/cheerio-parser.js";
|
|
8
|
-
import { NullCacheStore } from "../../../../src/adapters/cache/null-cache.js";
|
|
9
|
-
|
|
10
|
-
const FIXTURES_DIR = join(import.meta.dirname, "../../../fixtures");
|
|
11
|
-
|
|
12
|
-
function makeDeps(http: MockHttpClient) {
|
|
13
|
-
return { http, parser: new CheerioHtmlParser(), cache: new NullCacheStore() };
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
async function loadFixture(name: string): Promise<string> {
|
|
17
|
-
return readFile(join(FIXTURES_DIR, name), "utf-8");
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
describe("coronashaAdapter", () => {
|
|
21
|
-
describe("search()", () => {
|
|
22
|
-
it("電子版ありの書籍のみ返す(電子版なしは除外)", async () => {
|
|
23
|
-
const body = await loadFixture("coronasha-search.html");
|
|
24
|
-
const http = new MockHttpClient().addResponse(
|
|
25
|
-
"https://www.coronasha.co.jp/np/result.html",
|
|
26
|
-
{ status: 200, body },
|
|
27
|
-
);
|
|
28
|
-
|
|
29
|
-
const results = await coronashaAdapter.search({ title: "Julia" }, makeDeps(http));
|
|
30
|
-
|
|
31
|
-
// フィクスチャには電子版あり1件・電子版なし1件あり、電子版のみ返す
|
|
32
|
-
assert.strictEqual(results.length, 1);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it("タイトルを tunogaki + book-title で組み立てる", async () => {
|
|
36
|
-
const body = await loadFixture("coronasha-search.html");
|
|
37
|
-
const http = new MockHttpClient().addResponse(
|
|
38
|
-
"https://www.coronasha.co.jp/np/result.html",
|
|
39
|
-
{ status: 200, body },
|
|
40
|
-
);
|
|
41
|
-
|
|
42
|
-
const results = await coronashaAdapter.search({ title: "Julia" }, makeDeps(http));
|
|
43
|
-
|
|
44
|
-
assert.strictEqual(results[0].title, "1から始める Juliaプログラミング大全");
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it("著者一覧を返す", async () => {
|
|
48
|
-
const body = await loadFixture("coronasha-search.html");
|
|
49
|
-
const http = new MockHttpClient().addResponse(
|
|
50
|
-
"https://www.coronasha.co.jp/np/result.html",
|
|
51
|
-
{ status: 200, body },
|
|
52
|
-
);
|
|
53
|
-
|
|
54
|
-
const results = await coronashaAdapter.search({ title: "Julia" }, makeDeps(http));
|
|
55
|
-
|
|
56
|
-
assert.deepStrictEqual(results[0].authors, ["進藤 裕之", "佐藤 建太"]);
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it("価格・ISBN・発行日を返す", async () => {
|
|
60
|
-
const body = await loadFixture("coronasha-search.html");
|
|
61
|
-
const http = new MockHttpClient().addResponse(
|
|
62
|
-
"https://www.coronasha.co.jp/np/result.html",
|
|
63
|
-
{ status: 200, body },
|
|
64
|
-
);
|
|
65
|
-
|
|
66
|
-
const results = await coronashaAdapter.search({ title: "Julia" }, makeDeps(http));
|
|
67
|
-
|
|
68
|
-
assert.partialDeepStrictEqual(results[0], {
|
|
69
|
-
price: 3630,
|
|
70
|
-
isbn: "9784339029345",
|
|
71
|
-
publishedAt: "2023-05-01",
|
|
72
|
-
});
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
it("coverImageUrl が絶対URLになる", async () => {
|
|
76
|
-
const body = await loadFixture("coronasha-search.html");
|
|
77
|
-
const http = new MockHttpClient().addResponse(
|
|
78
|
-
"https://www.coronasha.co.jp/np/result.html",
|
|
79
|
-
{ status: 200, body },
|
|
80
|
-
);
|
|
81
|
-
|
|
82
|
-
const results = await coronashaAdapter.search({ title: "Julia" }, makeDeps(http));
|
|
83
|
-
|
|
84
|
-
assert.strictEqual(
|
|
85
|
-
results[0].coverImageUrl,
|
|
86
|
-
"https://www.coronasha.co.jp/np/images/isbn/9784339029345_main.jpg",
|
|
87
|
-
);
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
it("publisher が コロナ社 になる", async () => {
|
|
91
|
-
const body = await loadFixture("coronasha-search.html");
|
|
92
|
-
const http = new MockHttpClient().addResponse(
|
|
93
|
-
"https://www.coronasha.co.jp/np/result.html",
|
|
94
|
-
{ status: 200, body },
|
|
95
|
-
);
|
|
96
|
-
|
|
97
|
-
const results = await coronashaAdapter.search({ title: "Julia" }, makeDeps(http));
|
|
98
|
-
|
|
99
|
-
assert.strictEqual(results[0].publisher, "コロナ社");
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
it("title も author も空の場合は [] を返しHTTPを呼ばない", async () => {
|
|
103
|
-
const http = new MockHttpClient();
|
|
104
|
-
|
|
105
|
-
const results = await coronashaAdapter.search({}, makeDeps(http));
|
|
106
|
-
|
|
107
|
-
assert.deepStrictEqual(results, []);
|
|
108
|
-
assert.strictEqual(http.calls.length, 0);
|
|
109
|
-
});
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
describe("getDetail()", () => {
|
|
113
|
-
it("詳細情報を返す", async () => {
|
|
114
|
-
const body = await loadFixture("coronasha-detail.html");
|
|
115
|
-
const http = new MockHttpClient().addResponse(
|
|
116
|
-
"https://www.coronasha.co.jp/np/isbn/9784339029345/",
|
|
117
|
-
{ status: 200, body },
|
|
118
|
-
);
|
|
119
|
-
|
|
120
|
-
const book = await coronashaAdapter.getDetail(
|
|
121
|
-
"https://www.coronasha.co.jp/np/isbn/9784339029345/",
|
|
122
|
-
makeDeps(http),
|
|
123
|
-
);
|
|
124
|
-
|
|
125
|
-
assert.partialDeepStrictEqual(book, {
|
|
126
|
-
title: "1から始める Juliaプログラミング大全",
|
|
127
|
-
publisher: "コロナ社",
|
|
128
|
-
price: 3630,
|
|
129
|
-
publishedAt: "2023-05-01",
|
|
130
|
-
isbn: "9784339029345",
|
|
131
|
-
});
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
it("著者一覧を返す", async () => {
|
|
135
|
-
const body = await loadFixture("coronasha-detail.html");
|
|
136
|
-
const http = new MockHttpClient().addResponse(
|
|
137
|
-
"https://www.coronasha.co.jp/np/isbn/9784339029345/",
|
|
138
|
-
{ status: 200, body },
|
|
139
|
-
);
|
|
140
|
-
|
|
141
|
-
const book = await coronashaAdapter.getDetail(
|
|
142
|
-
"https://www.coronasha.co.jp/np/isbn/9784339029345/",
|
|
143
|
-
makeDeps(http),
|
|
144
|
-
);
|
|
145
|
-
|
|
146
|
-
assert.deepStrictEqual(book.authors, ["進藤 裕之", "佐藤 建太"]);
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
it("ebookStores に Kindle・Kinoppy・VarsityWave eBooks が含まれる", async () => {
|
|
150
|
-
const body = await loadFixture("coronasha-detail.html");
|
|
151
|
-
const http = new MockHttpClient().addResponse(
|
|
152
|
-
"https://www.coronasha.co.jp/np/isbn/9784339029345/",
|
|
153
|
-
{ status: 200, body },
|
|
154
|
-
);
|
|
155
|
-
|
|
156
|
-
const book = await coronashaAdapter.getDetail(
|
|
157
|
-
"https://www.coronasha.co.jp/np/isbn/9784339029345/",
|
|
158
|
-
makeDeps(http),
|
|
159
|
-
);
|
|
160
|
-
|
|
161
|
-
for (const { name, drm } of [
|
|
162
|
-
{ name: "Kindle", drm: "drm" },
|
|
163
|
-
{ name: "Kinoppy", drm: "drm" },
|
|
164
|
-
{ name: "VarsityWave eBooks", drm: "drm" },
|
|
165
|
-
]) {
|
|
166
|
-
assert.ok(
|
|
167
|
-
book.ebookStores.some(s => s.name === name && s.drm === drm),
|
|
168
|
-
`Expected store: ${name} (${drm})`,
|
|
169
|
-
);
|
|
170
|
-
}
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
it("Knowledge Worker (kw.maruzen.co.jp) は ebookStores に含まれない", async () => {
|
|
174
|
-
const body = await loadFixture("coronasha-detail.html");
|
|
175
|
-
const http = new MockHttpClient().addResponse(
|
|
176
|
-
"https://www.coronasha.co.jp/np/isbn/9784339029345/",
|
|
177
|
-
{ status: 200, body },
|
|
178
|
-
);
|
|
179
|
-
|
|
180
|
-
const book = await coronashaAdapter.getDetail(
|
|
181
|
-
"https://www.coronasha.co.jp/np/isbn/9784339029345/",
|
|
182
|
-
makeDeps(http),
|
|
183
|
-
);
|
|
184
|
-
|
|
185
|
-
const names = book.ebookStores.map(s => s.name);
|
|
186
|
-
assert.ok(!names.includes("Knowledge Worker"));
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
it("coverImageUrl が絶対URLになる", async () => {
|
|
190
|
-
const body = await loadFixture("coronasha-detail.html");
|
|
191
|
-
const http = new MockHttpClient().addResponse(
|
|
192
|
-
"https://www.coronasha.co.jp/np/isbn/9784339029345/",
|
|
193
|
-
{ status: 200, body },
|
|
194
|
-
);
|
|
195
|
-
|
|
196
|
-
const book = await coronashaAdapter.getDetail(
|
|
197
|
-
"https://www.coronasha.co.jp/np/isbn/9784339029345/",
|
|
198
|
-
makeDeps(http),
|
|
199
|
-
);
|
|
200
|
-
|
|
201
|
-
assert.strictEqual(
|
|
202
|
-
book.coverImageUrl,
|
|
203
|
-
"https://www.coronasha.co.jp/np/images/isbn/9784339029345_main.jpg",
|
|
204
|
-
);
|
|
205
|
-
});
|
|
206
|
-
});
|
|
207
|
-
});
|