@zonuexe/techbook-mcp 0.1.0 → 0.2.0

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.
Files changed (122) hide show
  1. package/.claude/settings.local.json +13 -1
  2. package/.codex/skills/techbook-mcp-release-prep/SKILL.md +105 -0
  3. package/.github/workflows/test.yml +36 -0
  4. package/.oxlintrc.json +12 -0
  5. package/AGENTS.md +29 -1
  6. package/CHANGELOG.md +27 -0
  7. package/deno.json +3 -0
  8. package/dist/adapters/html/cheerio-parser.d.ts.map +1 -1
  9. package/dist/adapters/html/cheerio-parser.js.map +1 -1
  10. package/dist/adapters/publishers/base.d.ts +22 -1
  11. package/dist/adapters/publishers/base.d.ts.map +1 -1
  12. package/dist/adapters/publishers/base.js +142 -2
  13. package/dist/adapters/publishers/base.js.map +1 -1
  14. package/dist/adapters/publishers/book-tech.d.ts +3 -0
  15. package/dist/adapters/publishers/book-tech.d.ts.map +1 -0
  16. package/dist/adapters/publishers/book-tech.js +95 -0
  17. package/dist/adapters/publishers/book-tech.js.map +1 -0
  18. package/dist/adapters/publishers/born-digital.d.ts +3 -0
  19. package/dist/adapters/publishers/born-digital.d.ts.map +1 -0
  20. package/dist/adapters/publishers/born-digital.js +122 -0
  21. package/dist/adapters/publishers/born-digital.js.map +1 -0
  22. package/dist/adapters/publishers/coronasha.d.ts +3 -0
  23. package/dist/adapters/publishers/coronasha.d.ts.map +1 -0
  24. package/dist/adapters/publishers/coronasha.js +119 -0
  25. package/dist/adapters/publishers/coronasha.js.map +1 -0
  26. package/dist/adapters/publishers/impress.d.ts +3 -0
  27. package/dist/adapters/publishers/impress.d.ts.map +1 -0
  28. package/dist/adapters/publishers/impress.js +92 -0
  29. package/dist/adapters/publishers/impress.js.map +1 -0
  30. package/dist/adapters/publishers/manatee.d.ts +3 -0
  31. package/dist/adapters/publishers/manatee.d.ts.map +1 -0
  32. package/dist/adapters/publishers/manatee.js +93 -0
  33. package/dist/adapters/publishers/manatee.js.map +1 -0
  34. package/dist/adapters/publishers/maruzen-publishing.d.ts +3 -0
  35. package/dist/adapters/publishers/maruzen-publishing.d.ts.map +1 -0
  36. package/dist/adapters/publishers/maruzen-publishing.js +108 -0
  37. package/dist/adapters/publishers/maruzen-publishing.js.map +1 -0
  38. package/dist/adapters/publishers/optronics.d.ts +3 -0
  39. package/dist/adapters/publishers/optronics.d.ts.map +1 -0
  40. package/dist/adapters/publishers/optronics.js +92 -0
  41. package/dist/adapters/publishers/optronics.js.map +1 -0
  42. package/dist/adapters/publishers/oreilly-japan.d.ts +3 -0
  43. package/dist/adapters/publishers/oreilly-japan.d.ts.map +1 -0
  44. package/dist/adapters/publishers/oreilly-japan.js +112 -0
  45. package/dist/adapters/publishers/oreilly-japan.js.map +1 -0
  46. package/dist/adapters/publishers/peaks.d.ts +3 -0
  47. package/dist/adapters/publishers/peaks.d.ts.map +1 -0
  48. package/dist/adapters/publishers/peaks.js +80 -0
  49. package/dist/adapters/publishers/peaks.js.map +1 -0
  50. package/dist/adapters/publishers/personal-media.d.ts +3 -0
  51. package/dist/adapters/publishers/personal-media.d.ts.map +1 -0
  52. package/dist/adapters/publishers/personal-media.js +144 -0
  53. package/dist/adapters/publishers/personal-media.js.map +1 -0
  54. package/dist/adapters/publishers/registry.d.ts.map +1 -1
  55. package/dist/adapters/publishers/registry.js +26 -0
  56. package/dist/adapters/publishers/registry.js.map +1 -1
  57. package/dist/adapters/publishers/rutles.d.ts +3 -0
  58. package/dist/adapters/publishers/rutles.d.ts.map +1 -0
  59. package/dist/adapters/publishers/rutles.js +128 -0
  60. package/dist/adapters/publishers/rutles.js.map +1 -0
  61. package/dist/adapters/publishers/saiensu.d.ts +3 -0
  62. package/dist/adapters/publishers/saiensu.d.ts.map +1 -0
  63. package/dist/adapters/publishers/saiensu.js +109 -0
  64. package/dist/adapters/publishers/saiensu.js.map +1 -0
  65. package/dist/adapters/publishers/seshop.d.ts +3 -0
  66. package/dist/adapters/publishers/seshop.d.ts.map +1 -0
  67. package/dist/adapters/publishers/seshop.js +98 -0
  68. package/dist/adapters/publishers/seshop.js.map +1 -0
  69. package/dist/application/get-book-detail.d.ts.map +1 -1
  70. package/dist/application/get-book-detail.js +5 -0
  71. package/dist/application/get-book-detail.js.map +1 -1
  72. package/dist/application/search-books.d.ts.map +1 -1
  73. package/dist/application/search-books.js +7 -1
  74. package/dist/application/search-books.js.map +1 -1
  75. package/dist/domain/book.d.ts +5 -4
  76. package/dist/domain/book.d.ts.map +1 -1
  77. package/dist/main.d.ts +1 -0
  78. package/dist/main.js +1 -0
  79. package/dist/main.js.map +1 -1
  80. package/dist/mcp/server.d.ts.map +1 -1
  81. package/dist/mcp/server.js +1 -0
  82. package/dist/mcp/server.js.map +1 -1
  83. package/flake.nix +1 -1
  84. package/package.json +7 -5
  85. package/src/adapters/html/cheerio-parser.ts +4 -3
  86. package/src/adapters/publishers/base.ts +150 -0
  87. package/src/adapters/publishers/born-digital.ts +2 -17
  88. package/src/adapters/publishers/impress.ts +103 -0
  89. package/src/adapters/publishers/manatee.ts +2 -1
  90. package/src/adapters/publishers/maruzen-publishing.ts +4 -16
  91. package/src/adapters/publishers/oreilly-japan.ts +5 -10
  92. package/src/adapters/publishers/registry.ts +2 -0
  93. package/src/adapters/publishers/rutles.ts +1 -13
  94. package/src/adapters/publishers/saiensu.ts +5 -18
  95. package/src/adapters/publishers/seshop.ts +1 -1
  96. package/src/application/get-book-detail.ts +7 -0
  97. package/src/application/search-books.ts +6 -1
  98. package/src/main.ts +1 -0
  99. package/tests/fixtures/impress-detail-epub.html +746 -0
  100. package/tests/fixtures/impress-detail-social.html +689 -0
  101. package/tests/unit/adapters/base.test.ts +441 -0
  102. package/tests/unit/adapters/publishers/book-tech.test.ts +18 -15
  103. package/tests/unit/adapters/publishers/born-digital.test.ts +18 -15
  104. package/tests/unit/adapters/publishers/coronasha.test.ts +26 -20
  105. package/tests/unit/adapters/publishers/gihyo.test.ts +21 -19
  106. package/tests/unit/adapters/publishers/impress.test.ts +129 -0
  107. package/tests/unit/adapters/publishers/lambdanote.test.ts +12 -11
  108. package/tests/unit/adapters/publishers/manatee.test.ts +14 -12
  109. package/tests/unit/adapters/publishers/maruzen-publishing.test.ts +19 -17
  110. package/tests/unit/adapters/publishers/optronics.test.ts +19 -16
  111. package/tests/unit/adapters/publishers/oreilly-japan.test.ts +19 -16
  112. package/tests/unit/adapters/publishers/peaks.test.ts +17 -14
  113. package/tests/unit/adapters/publishers/personal-media.test.ts +18 -15
  114. package/tests/unit/adapters/publishers/rutles.test.ts +15 -12
  115. package/tests/unit/adapters/publishers/saiensu.test.ts +14 -12
  116. package/tests/unit/adapters/publishers/seshop.test.ts +16 -13
  117. package/tests/unit/adapters/publishers/tatsu-zine.test.ts +13 -12
  118. package/tests/unit/adapters/publishers/techbookfest.test.ts +12 -11
  119. package/tests/unit/adapters/registry.test.ts +37 -0
  120. package/tests/unit/application/get-book-detail.test.ts +102 -0
  121. package/tests/unit/application/search-books.test.ts +137 -0
  122. package/vitest.config.ts +0 -8
@@ -1,4 +1,5 @@
1
- import { describe, it, expect } from "vitest";
1
+ import { describe, it } from "node:test";
2
+ import assert from "node:assert/strict";
2
3
  import { readFile } from "node:fs/promises";
3
4
  import { join } from "node:path";
4
5
  import { optronicsAdapter } from "../../../../src/adapters/publishers/optronics.js";
@@ -27,8 +28,8 @@ describe("optronicsAdapter", () => {
27
28
 
28
29
  const results = await optronicsAdapter.search({ title: "センシング" }, makeDeps(http));
29
30
 
30
- expect(results).toHaveLength(2);
31
- expect(results[0]).toMatchObject({
31
+ assert.strictEqual(results.length, 2);
32
+ assert.partialDeepStrictEqual(results[0], {
32
33
  title: "感性計測&感覚センサ技術集成",
33
34
  url: "https://optronics-ebook.com/products/detail.php?product_id=235",
34
35
  price: 15000,
@@ -44,7 +45,7 @@ describe("optronicsAdapter", () => {
44
45
 
45
46
  const results = await optronicsAdapter.search({ title: "センシング" }, makeDeps(http));
46
47
 
47
- expect(results[0].publisher).toBe("センシンディー株式会社");
48
+ assert.strictEqual(results[0].publisher, "センシンディー株式会社");
48
49
  });
49
50
 
50
51
  it("listcomment から著者を取得する", async () => {
@@ -57,7 +58,7 @@ describe("optronicsAdapter", () => {
57
58
  const results = await optronicsAdapter.search({ title: "センシング" }, makeDeps(http));
58
59
 
59
60
  // 2件目は著者フィールドあり
60
- expect(results[1].authors).toEqual(["波多腰 玄一"]);
61
+ assert.deepStrictEqual(results[1].authors, ["波多腰 玄一"]);
61
62
  });
62
63
 
63
64
  it("coverImageUrl が設定される", async () => {
@@ -69,7 +70,8 @@ describe("optronicsAdapter", () => {
69
70
 
70
71
  const results = await optronicsAdapter.search({ title: "センシング" }, makeDeps(http));
71
72
 
72
- expect(results[0].coverImageUrl).toBe(
73
+ assert.strictEqual(
74
+ results[0].coverImageUrl,
73
75
  "https://optronics-ebook.com/upload/save_image/03031025_69a6388916874.jpg",
74
76
  );
75
77
  });
@@ -83,7 +85,7 @@ describe("optronicsAdapter", () => {
83
85
 
84
86
  const results = await optronicsAdapter.search({ title: "センシング" }, makeDeps(http));
85
87
 
86
- expect(results[0].ebookStores).toEqual([
88
+ assert.deepStrictEqual(results[0].ebookStores, [
87
89
  {
88
90
  name: "オプトロニクス社",
89
91
  url: "https://optronics-ebook.com/products/detail.php?product_id=235",
@@ -97,8 +99,8 @@ describe("optronicsAdapter", () => {
97
99
 
98
100
  const results = await optronicsAdapter.search({}, makeDeps(http));
99
101
 
100
- expect(results).toEqual([]);
101
- expect(http.calls).toHaveLength(0);
102
+ assert.deepStrictEqual(results, []);
103
+ assert.strictEqual(http.calls.length, 0);
102
104
  });
103
105
 
104
106
  it("検索URLに name と category_id=1 が含まれる", async () => {
@@ -110,8 +112,8 @@ describe("optronicsAdapter", () => {
110
112
 
111
113
  await optronicsAdapter.search({ title: "センシング" }, makeDeps(http));
112
114
 
113
- expect(http.calls[0]).toContain("name=%E3%82%BB%E3%83%B3%E3%82%B7%E3%83%B3%E3%82%B0");
114
- expect(http.calls[0]).toContain("category_id=1");
115
+ assert.ok(http.calls[0].includes("name=%E3%82%BB%E3%83%B3%E3%82%B7%E3%83%B3%E3%82%B0"));
116
+ assert.ok(http.calls[0].includes("category_id=1"));
115
117
  });
116
118
  });
117
119
 
@@ -128,7 +130,7 @@ describe("optronicsAdapter", () => {
128
130
  makeDeps(http),
129
131
  );
130
132
 
131
- expect(book).toMatchObject({
133
+ assert.partialDeepStrictEqual(book, {
132
134
  title: "光センシング技術の最前線",
133
135
  price: 20000,
134
136
  });
@@ -146,7 +148,7 @@ describe("optronicsAdapter", () => {
146
148
  makeDeps(http),
147
149
  );
148
150
 
149
- expect(book.authors).toEqual(["波多腰 玄一"]);
151
+ assert.deepStrictEqual(book.authors, ["波多腰 玄一"]);
150
152
  });
151
153
 
152
154
  it("main_comment から発行元を取得し ㈱ を除去する", async () => {
@@ -161,7 +163,7 @@ describe("optronicsAdapter", () => {
161
163
  makeDeps(http),
162
164
  );
163
165
 
164
- expect(book.publisher).toBe("オプトロニクス社");
166
+ assert.strictEqual(book.publisher, "オプトロニクス社");
165
167
  });
166
168
 
167
169
  it("coverImageUrl が設定される", async () => {
@@ -176,7 +178,8 @@ describe("optronicsAdapter", () => {
176
178
  makeDeps(http),
177
179
  );
178
180
 
179
- expect(book.coverImageUrl).toBe(
181
+ assert.strictEqual(
182
+ book.coverImageUrl,
180
183
  "https://optronics-ebook.com/upload/save_image/11141121_636a1e0f7bd39.jpg",
181
184
  );
182
185
  });
@@ -193,7 +196,7 @@ describe("optronicsAdapter", () => {
193
196
  makeDeps(http),
194
197
  );
195
198
 
196
- expect(book.ebookStores).toEqual([
199
+ assert.deepStrictEqual(book.ebookStores, [
197
200
  {
198
201
  name: "オプトロニクス社",
199
202
  url: "https://optronics-ebook.com/products/detail.php?product_id=210",
@@ -1,4 +1,5 @@
1
- import { describe, it, expect } from "vitest";
1
+ import { describe, it } from "node:test";
2
+ import assert from "node:assert/strict";
2
3
  import { readFile } from "node:fs/promises";
3
4
  import { join } from "node:path";
4
5
  import { oreillyJapanAdapter } from "../../../../src/adapters/publishers/oreilly-japan.js";
@@ -27,8 +28,8 @@ describe("oreillyJapanAdapter", () => {
27
28
 
28
29
  const results = await oreillyJapanAdapter.search({ title: "TypeScript" }, makeDeps(http));
29
30
 
30
- expect(results).toHaveLength(1);
31
- expect(results[0]).toMatchObject({
31
+ assert.strictEqual(results.length, 1);
32
+ assert.partialDeepStrictEqual(results[0], {
32
33
  title: "Effective TypeScript 第2版",
33
34
  publisher: "オライリー・ジャパン",
34
35
  url: "https://www.oreilly.co.jp/books/9784814401093/",
@@ -47,7 +48,7 @@ describe("oreillyJapanAdapter", () => {
47
48
 
48
49
  const results = await oreillyJapanAdapter.search({ title: "TypeScript" }, makeDeps(http));
49
50
 
50
- expect(results[0].ebookStores).toEqual([
51
+ assert.deepStrictEqual(results[0].ebookStores, [
51
52
  {
52
53
  name: "オライリー・ジャパン",
53
54
  url: "https://www.oreilly.co.jp/books/9784814401093/",
@@ -65,7 +66,8 @@ describe("oreillyJapanAdapter", () => {
65
66
 
66
67
  const results = await oreillyJapanAdapter.search({ title: "TypeScript" }, makeDeps(http));
67
68
 
68
- expect(results[0].coverImageUrl).toBe(
69
+ assert.strictEqual(
70
+ results[0].coverImageUrl,
69
71
  "https://www.oreilly.co.jp/books/images/picture_large978-4-8144-0109-3.jpeg",
70
72
  );
71
73
  });
@@ -79,7 +81,7 @@ describe("oreillyJapanAdapter", () => {
79
81
 
80
82
  const results = await oreillyJapanAdapter.search({ title: "typescript" }, makeDeps(http));
81
83
 
82
- expect(results).toHaveLength(1);
84
+ assert.strictEqual(results.length, 1);
83
85
  });
84
86
 
85
87
  it("limit を適用する", async () => {
@@ -91,7 +93,7 @@ describe("oreillyJapanAdapter", () => {
91
93
 
92
94
  const results = await oreillyJapanAdapter.search({ title: "の", limit: 1 }, makeDeps(http));
93
95
 
94
- expect(results).toHaveLength(1);
96
+ assert.strictEqual(results.length, 1);
95
97
  });
96
98
 
97
99
  it("title が未指定の場合は [] を返しHTTPを呼ばない", async () => {
@@ -99,8 +101,8 @@ describe("oreillyJapanAdapter", () => {
99
101
 
100
102
  const results = await oreillyJapanAdapter.search({}, makeDeps(http));
101
103
 
102
- expect(results).toEqual([]);
103
- expect(http.calls).toHaveLength(0);
104
+ assert.deepStrictEqual(results, []);
105
+ assert.strictEqual(http.calls.length, 0);
104
106
  });
105
107
 
106
108
  it("author のみの場合も [] を返しHTTPを呼ばない", async () => {
@@ -108,8 +110,8 @@ describe("oreillyJapanAdapter", () => {
108
110
 
109
111
  const results = await oreillyJapanAdapter.search({ author: "Dan Vanderkam" }, makeDeps(http));
110
112
 
111
- expect(results).toEqual([]);
112
- expect(http.calls).toHaveLength(0);
113
+ assert.deepStrictEqual(results, []);
114
+ assert.strictEqual(http.calls.length, 0);
113
115
  });
114
116
  });
115
117
 
@@ -126,13 +128,13 @@ describe("oreillyJapanAdapter", () => {
126
128
  makeDeps(http),
127
129
  );
128
130
 
129
- expect(book).toMatchObject({
131
+ assert.partialDeepStrictEqual(book, {
130
132
  isbn: "9784814401093",
131
133
  price: 4620,
132
134
  publishedAt: "2025-04-08",
133
135
  publisher: "オライリー・ジャパン",
134
136
  });
135
- expect(book.title).toContain("Effective TypeScript 第2版");
137
+ assert.ok(book.title.includes("Effective TypeScript 第2版"));
136
138
  });
137
139
 
138
140
  it("著者が配列で返される(役割語を除去)", async () => {
@@ -147,7 +149,7 @@ describe("oreillyJapanAdapter", () => {
147
149
  makeDeps(http),
148
150
  );
149
151
 
150
- expect(book.authors).toEqual(["Dan Vanderkam", "今村 謙士"]);
152
+ assert.deepStrictEqual(book.authors, ["Dan Vanderkam", "今村 謙士"]);
151
153
  });
152
154
 
153
155
  it("coverImageUrl が取得される", async () => {
@@ -162,7 +164,8 @@ describe("oreillyJapanAdapter", () => {
162
164
  makeDeps(http),
163
165
  );
164
166
 
165
- expect(book.coverImageUrl).toBe(
167
+ assert.strictEqual(
168
+ book.coverImageUrl,
166
169
  "https://www.oreilly.co.jp/books/images/picture_large978-4-8144-0109-3.jpeg",
167
170
  );
168
171
  });
@@ -179,7 +182,7 @@ describe("oreillyJapanAdapter", () => {
179
182
  makeDeps(http),
180
183
  );
181
184
 
182
- expect(book.ebookStores).toEqual([
185
+ assert.deepStrictEqual(book.ebookStores, [
183
186
  {
184
187
  name: "オライリー・ジャパン",
185
188
  url: "https://www.oreilly.co.jp/books/9784814401093/",
@@ -1,4 +1,5 @@
1
- import { describe, it, expect } from "vitest";
1
+ import { describe, it } from "node:test";
2
+ import assert from "node:assert/strict";
2
3
  import { readFile } from "node:fs/promises";
3
4
  import { join } from "node:path";
4
5
  import { peaksAdapter } from "../../../../src/adapters/publishers/peaks.js";
@@ -27,8 +28,8 @@ describe("peaksAdapter", () => {
27
28
 
28
29
  const results = await peaksAdapter.search({ title: "Android" }, makeDeps(http));
29
30
 
30
- expect(results).toHaveLength(1);
31
- expect(results[0]).toMatchObject({
31
+ assert.strictEqual(results.length, 1);
32
+ assert.partialDeepStrictEqual(results[0], {
32
33
  title: "チームで育てるAndroidアプリ設計",
33
34
  publisher: "PEAKS",
34
35
  url: "https://peaks.cc/books/architecture_with_team",
@@ -44,7 +45,8 @@ describe("peaksAdapter", () => {
44
45
 
45
46
  const results = await peaksAdapter.search({ title: "Android" }, makeDeps(http));
46
47
 
47
- expect(results[0].coverImageUrl).toBe(
48
+ assert.strictEqual(
49
+ results[0].coverImageUrl,
48
50
  "https://peaks-img.s3-ap-northeast-1.amazonaws.com/architecture_with_team_book_cover_alpha.png",
49
51
  );
50
52
  });
@@ -58,7 +60,7 @@ describe("peaksAdapter", () => {
58
60
 
59
61
  const results = await peaksAdapter.search({ title: "Android" }, makeDeps(http));
60
62
 
61
- expect(results[0].ebookStores).toEqual([
63
+ assert.deepStrictEqual(results[0].ebookStores, [
62
64
  {
63
65
  name: "PEAKS",
64
66
  url: "https://peaks.cc/books/architecture_with_team",
@@ -72,8 +74,8 @@ describe("peaksAdapter", () => {
72
74
 
73
75
  const results = await peaksAdapter.search({}, makeDeps(http));
74
76
 
75
- expect(results).toEqual([]);
76
- expect(http.calls).toHaveLength(0);
77
+ assert.deepStrictEqual(results, []);
78
+ assert.strictEqual(http.calls.length, 0);
77
79
  });
78
80
 
79
81
  it("author のみの検索は [] を返しHTTPを呼ばない", async () => {
@@ -81,8 +83,8 @@ describe("peaksAdapter", () => {
81
83
 
82
84
  const results = await peaksAdapter.search({ author: "伊藤" }, makeDeps(http));
83
85
 
84
- expect(results).toEqual([]);
85
- expect(http.calls).toHaveLength(0);
86
+ assert.deepStrictEqual(results, []);
87
+ assert.strictEqual(http.calls.length, 0);
86
88
  });
87
89
 
88
90
  it("マッチしないキーワードは空配列を返す", async () => {
@@ -94,7 +96,7 @@ describe("peaksAdapter", () => {
94
96
 
95
97
  const results = await peaksAdapter.search({ title: "Python" }, makeDeps(http));
96
98
 
97
- expect(results).toEqual([]);
99
+ assert.deepStrictEqual(results, []);
98
100
  });
99
101
  });
100
102
 
@@ -111,7 +113,7 @@ describe("peaksAdapter", () => {
111
113
  makeDeps(http),
112
114
  );
113
115
 
114
- expect(book).toMatchObject({
116
+ assert.partialDeepStrictEqual(book, {
115
117
  title: "Jestではじめるテスト入門",
116
118
  publisher: "PEAKS",
117
119
  price: 2900,
@@ -130,7 +132,7 @@ describe("peaksAdapter", () => {
130
132
  makeDeps(http),
131
133
  );
132
134
 
133
- expect(book.authors).toEqual(["伊藤 貴之", "椎葉 光行"]);
135
+ assert.deepStrictEqual(book.authors, ["伊藤 貴之", "椎葉 光行"]);
134
136
  });
135
137
 
136
138
  it("coverImageUrl が設定される", async () => {
@@ -145,7 +147,8 @@ describe("peaksAdapter", () => {
145
147
  makeDeps(http),
146
148
  );
147
149
 
148
- expect(book.coverImageUrl).toBe(
150
+ assert.strictEqual(
151
+ book.coverImageUrl,
149
152
  "https://peaks-img.s3-ap-northeast-1.amazonaws.com/testing_with_jest_twittercard.png",
150
153
  );
151
154
  });
@@ -162,7 +165,7 @@ describe("peaksAdapter", () => {
162
165
  makeDeps(http),
163
166
  );
164
167
 
165
- expect(book.ebookStores).toEqual([
168
+ assert.deepStrictEqual(book.ebookStores, [
166
169
  {
167
170
  name: "PEAKS",
168
171
  url: "https://peaks.cc/books/testing_with_jest",
@@ -1,4 +1,5 @@
1
- import { describe, it, expect } from "vitest";
1
+ import { describe, it } from "node:test";
2
+ import assert from "node:assert/strict";
2
3
  import { readFile } from "node:fs/promises";
3
4
  import { join } from "node:path";
4
5
  import { personalMediaAdapter } from "../../../../src/adapters/publishers/personal-media.js";
@@ -28,7 +29,7 @@ describe("personalMediaAdapter", () => {
28
29
  const results = await personalMediaAdapter.search({ title: "TRON" }, makeDeps(http));
29
30
 
30
31
  // フィクスチャには TRON 含む2件・含まない1件あり
31
- expect(results).toHaveLength(2);
32
+ assert.strictEqual(results.length, 2);
32
33
  });
33
34
 
34
35
  it("書籍タイトルが正しく取得される", async () => {
@@ -40,7 +41,7 @@ describe("personalMediaAdapter", () => {
40
41
 
41
42
  const results = await personalMediaAdapter.search({ title: "TRON" }, makeDeps(http));
42
43
 
43
- expect(results[0].title).toBe("μITRON4.0標準ガイドブック(PDF版)");
44
+ assert.strictEqual(results[0].title, "μITRON4.0標準ガイドブック(PDF版)");
44
45
  });
45
46
 
46
47
  it("税込価格が取得される", async () => {
@@ -52,7 +53,7 @@ describe("personalMediaAdapter", () => {
52
53
 
53
54
  const results = await personalMediaAdapter.search({ title: "TRON" }, makeDeps(http));
54
55
 
55
- expect(results[0].price).toBe(2585);
56
+ assert.strictEqual(results[0].price, 2585);
56
57
  });
57
58
 
58
59
  it("url が書籍詳細ページの絶対URLになる", async () => {
@@ -64,7 +65,8 @@ describe("personalMediaAdapter", () => {
64
65
 
65
66
  const results = await personalMediaAdapter.search({ title: "TRON" }, makeDeps(http));
66
67
 
67
- expect(results[0].url).toBe(
68
+ assert.strictEqual(
69
+ results[0].url,
68
70
  "https://www.personal-media.co.jp/book/tron/191.html",
69
71
  );
70
72
  });
@@ -78,7 +80,7 @@ describe("personalMediaAdapter", () => {
78
80
 
79
81
  const results = await personalMediaAdapter.search({ title: "TRON" }, makeDeps(http));
80
82
 
81
- expect(results[0].publisher).toBe("パーソナルメディア");
83
+ assert.strictEqual(results[0].publisher, "パーソナルメディア");
82
84
  });
83
85
 
84
86
  it("title が空の場合は [] を返しHTTPを呼ばない", async () => {
@@ -86,8 +88,8 @@ describe("personalMediaAdapter", () => {
86
88
 
87
89
  const results = await personalMediaAdapter.search({}, makeDeps(http));
88
90
 
89
- expect(results).toEqual([]);
90
- expect(http.calls).toHaveLength(0);
91
+ assert.deepStrictEqual(results, []);
92
+ assert.strictEqual(http.calls.length, 0);
91
93
  });
92
94
 
93
95
  it("author のみ指定の場合も [] を返しHTTPを呼ばない", async () => {
@@ -95,8 +97,8 @@ describe("personalMediaAdapter", () => {
95
97
 
96
98
  const results = await personalMediaAdapter.search({ author: "坂村健" }, makeDeps(http));
97
99
 
98
- expect(results).toEqual([]);
99
- expect(http.calls).toHaveLength(0);
100
+ assert.deepStrictEqual(results, []);
101
+ assert.strictEqual(http.calls.length, 0);
100
102
  });
101
103
  });
102
104
 
@@ -113,7 +115,7 @@ describe("personalMediaAdapter", () => {
113
115
  makeDeps(http),
114
116
  );
115
117
 
116
- expect(book.title).toBe("μITRON4.0標準ガイドブック");
118
+ assert.strictEqual(book.title, "μITRON4.0標準ガイドブック");
117
119
  });
118
120
 
119
121
  it("著者名から役割語を除去して返す", async () => {
@@ -128,7 +130,7 @@ describe("personalMediaAdapter", () => {
128
130
  makeDeps(http),
129
131
  );
130
132
 
131
- expect(book.authors).toEqual(["坂村 健", "社団法人トロン協会"]);
133
+ assert.deepStrictEqual(book.authors, ["坂村 健", "社団法人トロン協会"]);
132
134
  });
133
135
 
134
136
  it("価格・ISBN・発行日を返す", async () => {
@@ -143,7 +145,7 @@ describe("personalMediaAdapter", () => {
143
145
  makeDeps(http),
144
146
  );
145
147
 
146
- expect(book).toMatchObject({
148
+ assert.partialDeepStrictEqual(book, {
147
149
  price: 3520,
148
150
  isbn: "9784893621917",
149
151
  publishedAt: "2001-11-01",
@@ -162,7 +164,8 @@ describe("personalMediaAdapter", () => {
162
164
  makeDeps(http),
163
165
  );
164
166
 
165
- expect(book.coverImageUrl).toBe(
167
+ assert.strictEqual(
168
+ book.coverImageUrl,
166
169
  "https://www.personal-media.co.jp/book/tron/images/191_l.jpg",
167
170
  );
168
171
  });
@@ -179,7 +182,7 @@ describe("personalMediaAdapter", () => {
179
182
  makeDeps(http),
180
183
  );
181
184
 
182
- expect(book.ebookStores).toEqual([
185
+ assert.deepStrictEqual(book.ebookStores, [
183
186
  {
184
187
  name: "パーソナルメディア",
185
188
  url: "https://www.personal-media.co.jp/webshop/book/20191.html",
@@ -1,4 +1,5 @@
1
- import { describe, it, expect } from "vitest";
1
+ import { describe, it } from "node:test";
2
+ import assert from "node:assert/strict";
2
3
  import { readFile } from "node:fs/promises";
3
4
  import { join } from "node:path";
4
5
  import { rutlesAdapter } from "../../../../src/adapters/publishers/rutles.js";
@@ -28,8 +29,8 @@ describe("rutlesAdapter", () => {
28
29
  const results = await rutlesAdapter.search({ title: "C言語" }, makeDeps(http));
29
30
 
30
31
  // フィクスチャには電子2件・紙1件、電子のみ返す
31
- expect(results).toHaveLength(2);
32
- expect(results[0]).toMatchObject({
32
+ assert.strictEqual(results.length, 2);
33
+ assert.partialDeepStrictEqual(results[0], {
33
34
  title: "【電子版】C言語3Dゲームプログラミング教室",
34
35
  publisher: "ラトルズ",
35
36
  url: "https://shop.rutles.net/?pid=173173393",
@@ -46,7 +47,8 @@ describe("rutlesAdapter", () => {
46
47
 
47
48
  const results = await rutlesAdapter.search({ title: "C言語" }, makeDeps(http));
48
49
 
49
- expect(results[0].coverImageUrl).toBe(
50
+ assert.strictEqual(
51
+ results[0].coverImageUrl,
50
52
  "https://img21.shop-pro.jp/PA01496/800/product/173173393_th.jpg",
51
53
  );
52
54
  });
@@ -60,7 +62,7 @@ describe("rutlesAdapter", () => {
60
62
 
61
63
  const results = await rutlesAdapter.search({ title: "C言語" }, makeDeps(http));
62
64
 
63
- expect(results[0].ebookStores).toEqual([
65
+ assert.deepStrictEqual(results[0].ebookStores, [
64
66
  {
65
67
  name: "ラトルズ",
66
68
  url: "https://shop.rutles.net/?pid=173173393",
@@ -74,8 +76,8 @@ describe("rutlesAdapter", () => {
74
76
 
75
77
  const results = await rutlesAdapter.search({}, makeDeps(http));
76
78
 
77
- expect(results).toEqual([]);
78
- expect(http.calls).toHaveLength(0);
79
+ assert.deepStrictEqual(results, []);
80
+ assert.strictEqual(http.calls.length, 0);
79
81
  });
80
82
 
81
83
  it("検索URLに EUC-JP エンコードされたキーワードが含まれる", async () => {
@@ -88,7 +90,7 @@ describe("rutlesAdapter", () => {
88
90
  await rutlesAdapter.search({ title: "言語" }, makeDeps(http));
89
91
 
90
92
  // "言語" の EUC-JP エンコード = %B8%C0%B8%EC
91
- expect(http.calls[0]).toContain("%B8%C0%B8%EC");
93
+ assert.ok(http.calls[0].includes("%B8%C0%B8%EC"));
92
94
  });
93
95
  });
94
96
 
@@ -105,7 +107,7 @@ describe("rutlesAdapter", () => {
105
107
  makeDeps(http),
106
108
  );
107
109
 
108
- expect(book).toMatchObject({
110
+ assert.partialDeepStrictEqual(book, {
109
111
  title: "【電子版】C言語3Dゲームプログラミング教室",
110
112
  publisher: "ラトルズ",
111
113
  isbn: "9784899774211",
@@ -126,7 +128,7 @@ describe("rutlesAdapter", () => {
126
128
  makeDeps(http),
127
129
  );
128
130
 
129
- expect(book.authors).toEqual(["大槻有一郎", "山田巧"]);
131
+ assert.deepStrictEqual(book.authors, ["大槻有一郎", "山田巧"]);
130
132
  });
131
133
 
132
134
  it("coverImageUrl が設定される", async () => {
@@ -141,7 +143,8 @@ describe("rutlesAdapter", () => {
141
143
  makeDeps(http),
142
144
  );
143
145
 
144
- expect(book.coverImageUrl).toBe(
146
+ assert.strictEqual(
147
+ book.coverImageUrl,
145
148
  "https://img21.shop-pro.jp/PA01496/800/product/173173393.jpg",
146
149
  );
147
150
  });
@@ -158,7 +161,7 @@ describe("rutlesAdapter", () => {
158
161
  makeDeps(http),
159
162
  );
160
163
 
161
- expect(book.ebookStores).toEqual([
164
+ assert.deepStrictEqual(book.ebookStores, [
162
165
  {
163
166
  name: "ラトルズ",
164
167
  url: "https://shop.rutles.net/?pid=173173393",
@@ -1,4 +1,5 @@
1
- import { describe, it, expect } from "vitest";
1
+ import { describe, it } from "node:test";
2
+ import assert from "node:assert/strict";
2
3
  import { readFile } from "node:fs/promises";
3
4
  import { join } from "node:path";
4
5
  import { saiensuAdapter } from "../../../../src/adapters/publishers/saiensu.js";
@@ -28,8 +29,8 @@ describe("saiensuAdapter", () => {
28
29
  const results = await saiensuAdapter.search({ title: "統計" }, makeDeps(http));
29
30
 
30
31
  // フィクスチャには電子1件・紙1件あり、電子のみ返す
31
- expect(results).toHaveLength(1);
32
- expect(results[0]).toMatchObject({
32
+ assert.strictEqual(results.length, 1);
33
+ assert.partialDeepStrictEqual(results[0], {
33
34
  title: "統計リテラシーI【電子版】 ―記述統計から推測統計へ",
34
35
  authors: ["堀井俊佑"],
35
36
  publisher: "サイエンス社",
@@ -49,7 +50,8 @@ describe("saiensuAdapter", () => {
49
50
 
50
51
  const results = await saiensuAdapter.search({ title: "統計" }, makeDeps(http));
51
52
 
52
- expect(results[0].coverImageUrl).toBe(
53
+ assert.strictEqual(
54
+ results[0].coverImageUrl,
53
55
  "https://www.saiensu.co.jp/bookThumbs/2026-978-4-7819-9049-1.jpg",
54
56
  );
55
57
  });
@@ -63,7 +65,7 @@ describe("saiensuAdapter", () => {
63
65
 
64
66
  const results = await saiensuAdapter.search({ title: "統計" }, makeDeps(http));
65
67
 
66
- expect(results[0].ebookStores).toEqual([
68
+ assert.deepStrictEqual(results[0].ebookStores, [
67
69
  {
68
70
  name: "サイエンス社",
69
71
  url: "https://www.saiensu.co.jp/search/?isbn=978-4-7819-9049-1&y=2026",
@@ -81,7 +83,7 @@ describe("saiensuAdapter", () => {
81
83
 
82
84
  const results = await saiensuAdapter.search({ title: "統計" }, makeDeps(http));
83
85
 
84
- expect(results[0].authors).toEqual(["堀井俊佑"]);
86
+ assert.deepStrictEqual(results[0].authors, ["堀井俊佑"]);
85
87
  });
86
88
 
87
89
  it("title も author も空の場合は [] を返しHTTPを呼ばない", async () => {
@@ -89,8 +91,8 @@ describe("saiensuAdapter", () => {
89
91
 
90
92
  const results = await saiensuAdapter.search({}, makeDeps(http));
91
93
 
92
- expect(results).toEqual([]);
93
- expect(http.calls).toHaveLength(0);
94
+ assert.deepStrictEqual(results, []);
95
+ assert.strictEqual(http.calls.length, 0);
94
96
  });
95
97
 
96
98
  it("検索リクエストに keyword が含まれる", async () => {
@@ -102,7 +104,7 @@ describe("saiensuAdapter", () => {
102
104
 
103
105
  await saiensuAdapter.search({ title: "意味論" }, makeDeps(http));
104
106
 
105
- expect(http.calls[0]).toContain("keyword=%E6%84%8F%E5%91%B3%E8%AB%96");
107
+ assert.ok(http.calls[0].includes("keyword=%E6%84%8F%E5%91%B3%E8%AB%96"));
106
108
  });
107
109
  });
108
110
 
@@ -119,7 +121,7 @@ describe("saiensuAdapter", () => {
119
121
  makeDeps(http),
120
122
  );
121
123
 
122
- expect(book).toMatchObject({
124
+ assert.partialDeepStrictEqual(book, {
123
125
  title: "統計リテラシーI【電子版】 ―記述統計から推測統計へ",
124
126
  publisher: "サイエンス社",
125
127
  isbn: "9784781990491",
@@ -140,7 +142,7 @@ describe("saiensuAdapter", () => {
140
142
  makeDeps(http),
141
143
  );
142
144
 
143
- expect(book.authors).toEqual(["堀井俊佑"]);
145
+ assert.deepStrictEqual(book.authors, ["堀井俊佑"]);
144
146
  });
145
147
 
146
148
  it("ebookStores にサイエンス社(DRM付き)が含まれる", async () => {
@@ -155,7 +157,7 @@ describe("saiensuAdapter", () => {
155
157
  makeDeps(http),
156
158
  );
157
159
 
158
- expect(book.ebookStores).toEqual([
160
+ assert.deepStrictEqual(book.ebookStores, [
159
161
  {
160
162
  name: "サイエンス社",
161
163
  url: "https://www.saiensu.co.jp/search/?isbn=978-4-7819-9049-1&y=2026",