@nitpicker/report-google-sheets 0.4.1 → 0.4.3
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/package.json +7 -4
- package/CHANGELOG.md +0 -8
- package/src/__tests__/api/create-sheets.api.ts +0 -234
- package/src/__tests__/api/helpers.ts +0 -148
- package/src/__tests__/api/sheets.api.ts +0 -217
- package/src/archive.ts +0 -29
- package/src/data/add-to-summary.ts +0 -10
- package/src/data/create-discrepancies.ts +0 -81
- package/src/data/create-image-list.ts +0 -74
- package/src/data/create-links.ts +0 -134
- package/src/data/create-page-list.ts +0 -472
- package/src/data/create-referrers-relational-table.ts +0 -115
- package/src/data/create-resources-relational-table.ts +0 -104
- package/src/data/create-resources.ts +0 -51
- package/src/data/create-violations.spec.ts +0 -95
- package/src/data/create-violations.ts +0 -47
- package/src/debug.ts +0 -7
- package/src/index.ts +0 -1
- package/src/load-config.spec.ts +0 -37
- package/src/load-config.ts +0 -17
- package/src/report.ts +0 -231
- package/src/reports/get-plugin-reports.spec.ts +0 -42
- package/src/reports/get-plugin-reports.ts +0 -24
- package/src/sheets/create-cell-data.ts +0 -1
- package/src/sheets/create-sheets.ts +0 -523
- package/src/sheets/default-cell-format.spec.ts +0 -13
- package/src/sheets/default-cell-format.ts +0 -8
- package/src/sheets/format.spec.ts +0 -17
- package/src/sheets/format.ts +0 -14
- package/src/sheets/types.ts +0 -106
- package/src/types.ts +0 -11
- package/src/utils/has-prop-filter.spec.ts +0 -25
- package/src/utils/has-prop-filter.ts +0 -21
- package/src/utils/non-null-filter.spec.ts +0 -27
- package/src/utils/non-null-filter.ts +0 -15
- package/tsconfig.json +0 -11
- package/tsconfig.tsbuildinfo +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nitpicker/report-google-sheets",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.3",
|
|
4
4
|
"description": "Nitpicker reporter that outputs crawl and analysis results to Google Sheets",
|
|
5
5
|
"author": "D-ZERO",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -12,6 +12,9 @@
|
|
|
12
12
|
"publishConfig": {
|
|
13
13
|
"access": "public"
|
|
14
14
|
},
|
|
15
|
+
"files": [
|
|
16
|
+
"lib"
|
|
17
|
+
],
|
|
15
18
|
"type": "module",
|
|
16
19
|
"exports": {
|
|
17
20
|
".": {
|
|
@@ -28,8 +31,8 @@
|
|
|
28
31
|
"@d-zero/google-auth": "0.5.6",
|
|
29
32
|
"@d-zero/google-sheets": "0.6.0",
|
|
30
33
|
"@d-zero/shared": "0.20.0",
|
|
31
|
-
"@nitpicker/crawler": "0.4.
|
|
32
|
-
"@nitpicker/types": "0.4.
|
|
34
|
+
"@nitpicker/crawler": "0.4.3",
|
|
35
|
+
"@nitpicker/types": "0.4.3",
|
|
33
36
|
"ansi-colors": "4.1.3",
|
|
34
37
|
"debug": "4.4.3",
|
|
35
38
|
"enquirer": "2.4.1",
|
|
@@ -41,5 +44,5 @@
|
|
|
41
44
|
"googleapis": "171.4.0",
|
|
42
45
|
"vitest": "4.0.18"
|
|
43
46
|
},
|
|
44
|
-
"gitHead": "
|
|
47
|
+
"gitHead": "0f4ca55751be2f83dd5b6622c3502503fc7dfb41"
|
|
45
48
|
}
|
package/CHANGELOG.md
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
# Change Log
|
|
2
|
-
|
|
3
|
-
All notable changes to this project will be documented in this file.
|
|
4
|
-
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
|
-
|
|
6
|
-
## [0.4.1](https://github.com/d-zero-dev/nitpicker/compare/v0.4.0...v0.4.1) (2026-02-27)
|
|
7
|
-
|
|
8
|
-
**Note:** Version bump only for package @nitpicker/report-google-sheets
|
|
@@ -1,234 +0,0 @@
|
|
|
1
|
-
import type { Auth } from '@d-zero/google-auth';
|
|
2
|
-
import type { Report } from '@nitpicker/types';
|
|
3
|
-
|
|
4
|
-
import { Sheets } from '@d-zero/google-sheets';
|
|
5
|
-
import { google } from 'googleapis';
|
|
6
|
-
import { afterAll, afterEach, beforeAll, describe, expect, it } from 'vitest';
|
|
7
|
-
|
|
8
|
-
import { createLinks } from '../../data/create-links.js';
|
|
9
|
-
import { createPageList } from '../../data/create-page-list.js';
|
|
10
|
-
import { createResources } from '../../data/create-resources.js';
|
|
11
|
-
import { createViolations } from '../../data/create-violations.js';
|
|
12
|
-
import { createSheets } from '../../sheets/create-sheets.js';
|
|
13
|
-
|
|
14
|
-
import {
|
|
15
|
-
type CrawlResult,
|
|
16
|
-
SPREADSHEET_ID,
|
|
17
|
-
SPREADSHEET_URL,
|
|
18
|
-
cleanupCrawl,
|
|
19
|
-
crawlTestServer,
|
|
20
|
-
deleteSheet,
|
|
21
|
-
getAuth,
|
|
22
|
-
readSheetValues,
|
|
23
|
-
testSheetName,
|
|
24
|
-
} from './helpers.js';
|
|
25
|
-
|
|
26
|
-
describe('createSheets pipeline', () => {
|
|
27
|
-
let auth: Auth;
|
|
28
|
-
let crawlResult: CrawlResult;
|
|
29
|
-
const createdSheetIds: number[] = [];
|
|
30
|
-
const emptyReports: Report[] = [];
|
|
31
|
-
|
|
32
|
-
beforeAll(async () => {
|
|
33
|
-
auth = await getAuth();
|
|
34
|
-
crawlResult = await crawlTestServer(['http://localhost:8010/'], {
|
|
35
|
-
recursive: true,
|
|
36
|
-
fetchExternal: false,
|
|
37
|
-
});
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
afterEach(async () => {
|
|
41
|
-
for (const sheetId of createdSheetIds) {
|
|
42
|
-
try {
|
|
43
|
-
await deleteSheet(auth, SPREADSHEET_ID, sheetId);
|
|
44
|
-
} catch {
|
|
45
|
-
// already deleted or not found
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
createdSheetIds.length = 0;
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
afterAll(async () => {
|
|
52
|
-
if (crawlResult) {
|
|
53
|
-
await cleanupCrawl(crawlResult);
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it('Violations シートを正しく生成できる', async () => {
|
|
58
|
-
const sheetName = testSheetName('violations');
|
|
59
|
-
const sheets = new Sheets(SPREADSHEET_URL, auth);
|
|
60
|
-
|
|
61
|
-
const createTestViolations = () => {
|
|
62
|
-
const setting = createViolations(emptyReports);
|
|
63
|
-
return { ...setting, name: sheetName };
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
await createSheets({
|
|
67
|
-
sheets,
|
|
68
|
-
archive: crawlResult.archive,
|
|
69
|
-
reports: emptyReports,
|
|
70
|
-
limit: 100,
|
|
71
|
-
createSheetList: [createTestViolations],
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
const sheet = await sheets.create(sheetName);
|
|
75
|
-
createdSheetIds.push(sheet.id);
|
|
76
|
-
|
|
77
|
-
// Verify headers
|
|
78
|
-
const values = await readSheetValues(auth, SPREADSHEET_ID, `'${sheetName}'!A1:F1`);
|
|
79
|
-
expect(values[0]).toEqual([
|
|
80
|
-
'Validator',
|
|
81
|
-
'Severity',
|
|
82
|
-
'Rule',
|
|
83
|
-
'Code',
|
|
84
|
-
'Message',
|
|
85
|
-
'URL',
|
|
86
|
-
]);
|
|
87
|
-
|
|
88
|
-
// No data rows since reports are empty
|
|
89
|
-
const allValues = await readSheetValues(
|
|
90
|
-
auth,
|
|
91
|
-
SPREADSHEET_ID,
|
|
92
|
-
`'${sheetName}'!A1:F100`,
|
|
93
|
-
);
|
|
94
|
-
expect(allValues.length).toBe(1); // header only
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
it('Page List シートを正しく生成できる', async () => {
|
|
98
|
-
const sheetName = testSheetName('pagelist');
|
|
99
|
-
const sheets = new Sheets(SPREADSHEET_URL, auth);
|
|
100
|
-
|
|
101
|
-
const createTestPageList = () => {
|
|
102
|
-
const setting = createPageList(emptyReports);
|
|
103
|
-
return { ...setting, name: sheetName };
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
await createSheets({
|
|
107
|
-
sheets,
|
|
108
|
-
archive: crawlResult.archive,
|
|
109
|
-
reports: emptyReports,
|
|
110
|
-
limit: 100,
|
|
111
|
-
createSheetList: [createTestPageList],
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
const sheet = await sheets.create(sheetName);
|
|
115
|
-
createdSheetIds.push(sheet.id);
|
|
116
|
-
|
|
117
|
-
// Verify headers contain expected columns
|
|
118
|
-
const headerValues = await readSheetValues(
|
|
119
|
-
auth,
|
|
120
|
-
SPREADSHEET_ID,
|
|
121
|
-
`'${sheetName}'!A1:AK1`,
|
|
122
|
-
);
|
|
123
|
-
const headers = headerValues[0]!;
|
|
124
|
-
expect(headers).toContain('Title');
|
|
125
|
-
expect(headers).toContain('URL');
|
|
126
|
-
expect(headers).toContain('Status Code');
|
|
127
|
-
expect(headers).toContain('Internal Links');
|
|
128
|
-
expect(headers).toContain('External Links');
|
|
129
|
-
|
|
130
|
-
// Verify data rows exist (at least 1 page from test server)
|
|
131
|
-
const allValues = await readSheetValues(
|
|
132
|
-
auth,
|
|
133
|
-
SPREADSHEET_ID,
|
|
134
|
-
`'${sheetName}'!A1:A100`,
|
|
135
|
-
);
|
|
136
|
-
expect(allValues.length).toBeGreaterThan(1); // header + at least 1 data row
|
|
137
|
-
|
|
138
|
-
// Verify frozen panes and conditional format were applied
|
|
139
|
-
const sheetsApi = google.sheets({ version: 'v4', auth: auth as unknown });
|
|
140
|
-
const res = await sheetsApi.spreadsheets.get({
|
|
141
|
-
spreadsheetId: SPREADSHEET_ID,
|
|
142
|
-
includeGridData: false,
|
|
143
|
-
fields: 'sheets(properties,conditionalFormats)',
|
|
144
|
-
});
|
|
145
|
-
const targetSheet = res.data.sheets?.find((s) => s.properties?.sheetId === sheet.id);
|
|
146
|
-
|
|
147
|
-
expect(targetSheet?.properties?.gridProperties?.frozenColumnCount).toBe(1);
|
|
148
|
-
expect(targetSheet?.properties?.gridProperties?.frozenRowCount).toBe(1);
|
|
149
|
-
expect(targetSheet?.conditionalFormats).toBeDefined();
|
|
150
|
-
expect(targetSheet!.conditionalFormats!.length).toBeGreaterThanOrEqual(1);
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
it('Links シートを正しく生成できる', async () => {
|
|
154
|
-
const sheetName = testSheetName('links');
|
|
155
|
-
const sheets = new Sheets(SPREADSHEET_URL, auth);
|
|
156
|
-
|
|
157
|
-
const createTestLinks = () => {
|
|
158
|
-
const setting = createLinks(emptyReports);
|
|
159
|
-
return { ...setting, name: sheetName };
|
|
160
|
-
};
|
|
161
|
-
|
|
162
|
-
await createSheets({
|
|
163
|
-
sheets,
|
|
164
|
-
archive: crawlResult.archive,
|
|
165
|
-
reports: emptyReports,
|
|
166
|
-
limit: 100,
|
|
167
|
-
createSheetList: [createTestLinks],
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
const sheet = await sheets.create(sheetName);
|
|
171
|
-
createdSheetIds.push(sheet.id);
|
|
172
|
-
|
|
173
|
-
// Verify headers
|
|
174
|
-
const headerValues = await readSheetValues(
|
|
175
|
-
auth,
|
|
176
|
-
SPREADSHEET_ID,
|
|
177
|
-
`'${sheetName}'!A1:H1`,
|
|
178
|
-
);
|
|
179
|
-
expect(headerValues[0]).toEqual([
|
|
180
|
-
'URL',
|
|
181
|
-
'Page Title',
|
|
182
|
-
'Status Code',
|
|
183
|
-
'Status Text',
|
|
184
|
-
'Content Type',
|
|
185
|
-
'Redirect From',
|
|
186
|
-
'Referrers',
|
|
187
|
-
'Headers',
|
|
188
|
-
]);
|
|
189
|
-
|
|
190
|
-
// Verify data rows exist (links from test server pages)
|
|
191
|
-
const allValues = await readSheetValues(
|
|
192
|
-
auth,
|
|
193
|
-
SPREADSHEET_ID,
|
|
194
|
-
`'${sheetName}'!A1:A100`,
|
|
195
|
-
);
|
|
196
|
-
expect(allValues.length).toBeGreaterThan(1);
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
it('Resources シートを正しく生成できる', async () => {
|
|
200
|
-
const sheetName = testSheetName('resources');
|
|
201
|
-
const sheets = new Sheets(SPREADSHEET_URL, auth);
|
|
202
|
-
|
|
203
|
-
const createTestResources = () => {
|
|
204
|
-
const setting = createResources(emptyReports);
|
|
205
|
-
return { ...setting, name: sheetName };
|
|
206
|
-
};
|
|
207
|
-
|
|
208
|
-
await createSheets({
|
|
209
|
-
sheets,
|
|
210
|
-
archive: crawlResult.archive,
|
|
211
|
-
reports: emptyReports,
|
|
212
|
-
limit: 100,
|
|
213
|
-
createSheetList: [createTestResources],
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
const sheet = await sheets.create(sheetName);
|
|
217
|
-
createdSheetIds.push(sheet.id);
|
|
218
|
-
|
|
219
|
-
// Verify headers
|
|
220
|
-
const headerValues = await readSheetValues(
|
|
221
|
-
auth,
|
|
222
|
-
SPREADSHEET_ID,
|
|
223
|
-
`'${sheetName}'!A1:F1`,
|
|
224
|
-
);
|
|
225
|
-
expect(headerValues[0]).toEqual([
|
|
226
|
-
'URL',
|
|
227
|
-
'Status Code',
|
|
228
|
-
'Status Text',
|
|
229
|
-
'Content Type',
|
|
230
|
-
'Content Length',
|
|
231
|
-
'Referrers',
|
|
232
|
-
]);
|
|
233
|
-
});
|
|
234
|
-
});
|
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
import type { Auth } from '@d-zero/google-auth';
|
|
2
|
-
import type { Archive } from '@nitpicker/crawler';
|
|
3
|
-
import type { sheets_v4 } from 'googleapis';
|
|
4
|
-
|
|
5
|
-
import crypto from 'node:crypto';
|
|
6
|
-
import fs from 'node:fs/promises';
|
|
7
|
-
import os from 'node:os';
|
|
8
|
-
import path from 'node:path';
|
|
9
|
-
|
|
10
|
-
import { authentication } from '@d-zero/google-auth';
|
|
11
|
-
import { CrawlerOrchestrator } from '@nitpicker/crawler';
|
|
12
|
-
import { google } from 'googleapis';
|
|
13
|
-
|
|
14
|
-
const SPREADSHEET_ID = (() => {
|
|
15
|
-
const id = process.env.TEST_SPREADSHEET_ID;
|
|
16
|
-
if (!id) {
|
|
17
|
-
throw new Error('TEST_SPREADSHEET_ID environment variable is required for API tests');
|
|
18
|
-
}
|
|
19
|
-
return id;
|
|
20
|
-
})();
|
|
21
|
-
const SPREADSHEET_URL = `https://docs.google.com/spreadsheets/d/${SPREADSHEET_ID}/edit`;
|
|
22
|
-
const SCOPES = ['https://www.googleapis.com/auth/spreadsheets'] as const;
|
|
23
|
-
|
|
24
|
-
export { SPREADSHEET_ID, SPREADSHEET_URL };
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
*
|
|
28
|
-
*/
|
|
29
|
-
export async function getAuth(): Promise<Auth> {
|
|
30
|
-
const credentialFilePath = process.env.TEST_CREDENTIALS_PATH ?? 'credentials.json';
|
|
31
|
-
const tokenFilePath = process.env.TEST_TOKEN_PATH ?? 'token.json';
|
|
32
|
-
return authentication(credentialFilePath, SCOPES, { tokenFilePath });
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
*
|
|
37
|
-
* @param auth
|
|
38
|
-
* @param spreadsheetId
|
|
39
|
-
* @param sheetId
|
|
40
|
-
*/
|
|
41
|
-
export async function deleteSheet(
|
|
42
|
-
auth: Auth,
|
|
43
|
-
spreadsheetId: string,
|
|
44
|
-
sheetId: number,
|
|
45
|
-
): Promise<void> {
|
|
46
|
-
const sheets = google.sheets({ version: 'v4', auth: auth as unknown });
|
|
47
|
-
await sheets.spreadsheets.batchUpdate({
|
|
48
|
-
spreadsheetId,
|
|
49
|
-
requestBody: {
|
|
50
|
-
requests: [{ deleteSheet: { sheetId } }],
|
|
51
|
-
},
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
*
|
|
57
|
-
* @param auth
|
|
58
|
-
* @param spreadsheetId
|
|
59
|
-
* @param range
|
|
60
|
-
*/
|
|
61
|
-
export async function readSheetValues(
|
|
62
|
-
auth: Auth,
|
|
63
|
-
spreadsheetId: string,
|
|
64
|
-
range: string,
|
|
65
|
-
): Promise<string[][]> {
|
|
66
|
-
const sheets = google.sheets({ version: 'v4', auth: auth as unknown });
|
|
67
|
-
const res = await sheets.spreadsheets.values.get({
|
|
68
|
-
spreadsheetId,
|
|
69
|
-
range,
|
|
70
|
-
});
|
|
71
|
-
return (res.data.values as string[][]) || [];
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
*
|
|
76
|
-
* @param auth
|
|
77
|
-
* @param spreadsheetId
|
|
78
|
-
* @param sheetId
|
|
79
|
-
*/
|
|
80
|
-
export async function getSheetProperties(
|
|
81
|
-
auth: Auth,
|
|
82
|
-
spreadsheetId: string,
|
|
83
|
-
sheetId: number,
|
|
84
|
-
): Promise<sheets_v4.Schema$Sheet | undefined> {
|
|
85
|
-
const sheets = google.sheets({ version: 'v4', auth: auth as unknown });
|
|
86
|
-
const res = await sheets.spreadsheets.get({
|
|
87
|
-
spreadsheetId,
|
|
88
|
-
includeGridData: false,
|
|
89
|
-
});
|
|
90
|
-
return res.data.sheets?.find((s) => s.properties?.sheetId === sheetId);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
*
|
|
95
|
-
* @param prefix
|
|
96
|
-
*/
|
|
97
|
-
export function testSheetName(prefix: string): string {
|
|
98
|
-
return `__test_${prefix}_${Date.now()}`;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
export interface CrawlResult {
|
|
102
|
-
archive: Archive;
|
|
103
|
-
tmpDir: string;
|
|
104
|
-
cwd: string;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
*
|
|
109
|
-
* @param urls
|
|
110
|
-
* @param options
|
|
111
|
-
*/
|
|
112
|
-
export async function crawlTestServer(
|
|
113
|
-
urls: string[],
|
|
114
|
-
options?: Record<string, unknown>,
|
|
115
|
-
): Promise<CrawlResult> {
|
|
116
|
-
const cwd = path.join(os.tmpdir(), `nitpicker-api-${crypto.randomUUID()}`);
|
|
117
|
-
await fs.mkdir(cwd, { recursive: true });
|
|
118
|
-
|
|
119
|
-
const orchestrator = await CrawlerOrchestrator.crawling(
|
|
120
|
-
urls,
|
|
121
|
-
{
|
|
122
|
-
cwd,
|
|
123
|
-
interval: 0,
|
|
124
|
-
parallels: 1,
|
|
125
|
-
image: false,
|
|
126
|
-
...options,
|
|
127
|
-
},
|
|
128
|
-
(q) => {
|
|
129
|
-
q.on('error', (e) => {
|
|
130
|
-
console.error('[nitpicker:api-test] error:', e); // eslint-disable-line no-console
|
|
131
|
-
});
|
|
132
|
-
},
|
|
133
|
-
);
|
|
134
|
-
|
|
135
|
-
return {
|
|
136
|
-
archive: orchestrator.archive,
|
|
137
|
-
tmpDir: orchestrator.archive.tmpDir,
|
|
138
|
-
cwd,
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
*
|
|
144
|
-
* @param result
|
|
145
|
-
*/
|
|
146
|
-
export async function cleanupCrawl(result: CrawlResult) {
|
|
147
|
-
await fs.rm(result.cwd, { recursive: true, force: true });
|
|
148
|
-
}
|
|
@@ -1,217 +0,0 @@
|
|
|
1
|
-
import type { Auth } from '@d-zero/google-auth';
|
|
2
|
-
|
|
3
|
-
import { Sheets } from '@d-zero/google-sheets';
|
|
4
|
-
import { google } from 'googleapis';
|
|
5
|
-
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
|
|
6
|
-
|
|
7
|
-
import { createCellData } from '../../sheets/create-cell-data.js';
|
|
8
|
-
import { defaultCellFormat } from '../../sheets/default-cell-format.js';
|
|
9
|
-
|
|
10
|
-
import {
|
|
11
|
-
SPREADSHEET_ID,
|
|
12
|
-
SPREADSHEET_URL,
|
|
13
|
-
deleteSheet,
|
|
14
|
-
getAuth,
|
|
15
|
-
getSheetProperties,
|
|
16
|
-
readSheetValues,
|
|
17
|
-
testSheetName,
|
|
18
|
-
} from './helpers.js';
|
|
19
|
-
|
|
20
|
-
describe('Sheets class', () => {
|
|
21
|
-
let auth: Auth;
|
|
22
|
-
let sheets: Sheets;
|
|
23
|
-
const createdSheetIds: number[] = [];
|
|
24
|
-
|
|
25
|
-
beforeAll(async () => {
|
|
26
|
-
auth = await getAuth();
|
|
27
|
-
sheets = new Sheets(SPREADSHEET_URL, auth);
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
afterAll(async () => {
|
|
31
|
-
for (const sheetId of createdSheetIds) {
|
|
32
|
-
try {
|
|
33
|
-
await deleteSheet(auth, SPREADSHEET_ID, sheetId);
|
|
34
|
-
} catch {
|
|
35
|
-
// already deleted or not found
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it('create() で新規シートを作成できる', async () => {
|
|
41
|
-
const name = testSheetName('new');
|
|
42
|
-
const sheet = await sheets.create(name);
|
|
43
|
-
createdSheetIds.push(sheet.id);
|
|
44
|
-
|
|
45
|
-
expect(sheet.id).toBeTypeOf('number');
|
|
46
|
-
expect(sheet.props.title).toBe(name);
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it('create() で既存シートを返す(キャッシュ)', async () => {
|
|
50
|
-
const name = testSheetName('cache');
|
|
51
|
-
const sheet1 = await sheets.create(name);
|
|
52
|
-
createdSheetIds.push(sheet1.id);
|
|
53
|
-
|
|
54
|
-
const sheet2 = await sheets.create(name);
|
|
55
|
-
|
|
56
|
-
expect(sheet2.id).toBe(sheet1.id);
|
|
57
|
-
});
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
describe('Sheet class — addRowData', () => {
|
|
61
|
-
let auth: Auth;
|
|
62
|
-
let sheets: Sheets;
|
|
63
|
-
let sheetName: string;
|
|
64
|
-
let sheetId: number;
|
|
65
|
-
|
|
66
|
-
beforeAll(async () => {
|
|
67
|
-
auth = await getAuth();
|
|
68
|
-
sheets = new Sheets(SPREADSHEET_URL, auth);
|
|
69
|
-
|
|
70
|
-
// Use a single sheet for all addRowData tests
|
|
71
|
-
sheetName = testSheetName('rowdata');
|
|
72
|
-
const sheet = await sheets.create(sheetName);
|
|
73
|
-
sheetId = sheet.id;
|
|
74
|
-
|
|
75
|
-
// Write header
|
|
76
|
-
const headers = ['String', 'Number', 'Boolean', 'Null', 'Formula', 'Link'];
|
|
77
|
-
const headerRow = headers.map((h) =>
|
|
78
|
-
createCellData({ value: h, textFormat: { bold: true } }, defaultCellFormat),
|
|
79
|
-
);
|
|
80
|
-
await sheet.addRowData([headerRow], false);
|
|
81
|
-
|
|
82
|
-
// Write data row
|
|
83
|
-
const dataRow = [
|
|
84
|
-
createCellData({ value: 'hello world' }, defaultCellFormat),
|
|
85
|
-
createCellData({ value: 42 }, defaultCellFormat),
|
|
86
|
-
createCellData({ value: true }, defaultCellFormat),
|
|
87
|
-
createCellData({ value: null }, defaultCellFormat),
|
|
88
|
-
createCellData({ value: '=1+2' }, defaultCellFormat),
|
|
89
|
-
createCellData(
|
|
90
|
-
{
|
|
91
|
-
value: 'https://example.com',
|
|
92
|
-
textFormat: { link: { uri: 'https://example.com' } },
|
|
93
|
-
},
|
|
94
|
-
defaultCellFormat,
|
|
95
|
-
),
|
|
96
|
-
];
|
|
97
|
-
await sheet.addRowData([dataRow], true);
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
afterAll(async () => {
|
|
101
|
-
try {
|
|
102
|
-
await deleteSheet(auth, SPREADSHEET_ID, sheetId);
|
|
103
|
-
} catch {
|
|
104
|
-
// ignore
|
|
105
|
-
}
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
it('ヘッダー行が正しく書き込まれている', async () => {
|
|
109
|
-
const values = await readSheetValues(auth, SPREADSHEET_ID, `'${sheetName}'!A1:F1`);
|
|
110
|
-
expect(values[0]).toEqual(['String', 'Number', 'Boolean', 'Null', 'Formula', 'Link']);
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
it('データ行の各セル型が正しく書き込まれている', async () => {
|
|
114
|
-
const values = await readSheetValues(auth, SPREADSHEET_ID, `'${sheetName}'!A2:F2`);
|
|
115
|
-
const row = values[0]!;
|
|
116
|
-
expect(row[0]).toBe('hello world');
|
|
117
|
-
expect(row[1]).toBe('42');
|
|
118
|
-
expect(row[2]).toBe('TRUE');
|
|
119
|
-
expect(row[3]).toBe(''); // null renders as empty string
|
|
120
|
-
expect(row[4]).toBe('3'); // =1+2 evaluated
|
|
121
|
-
expect(row[5]).toBe('https://example.com'); // HYPERLINK shows decoded URL
|
|
122
|
-
});
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
describe('Sheet class — formatting', () => {
|
|
126
|
-
let auth: Auth;
|
|
127
|
-
let sheets: Sheets;
|
|
128
|
-
let sheetName: string;
|
|
129
|
-
let sheetId: number;
|
|
130
|
-
|
|
131
|
-
beforeAll(async () => {
|
|
132
|
-
auth = await getAuth();
|
|
133
|
-
sheets = new Sheets(SPREADSHEET_URL, auth);
|
|
134
|
-
|
|
135
|
-
// Use a single sheet for all formatting tests
|
|
136
|
-
sheetName = testSheetName('format');
|
|
137
|
-
const sheet = await sheets.create(sheetName);
|
|
138
|
-
sheetId = sheet.id;
|
|
139
|
-
|
|
140
|
-
// Write enough data to enable frozen/hideCol
|
|
141
|
-
const headerRow = [
|
|
142
|
-
createCellData({ value: 'Col1' }, defaultCellFormat),
|
|
143
|
-
createCellData({ value: 'Col2' }, defaultCellFormat),
|
|
144
|
-
createCellData({ value: 'Col3' }, defaultCellFormat),
|
|
145
|
-
];
|
|
146
|
-
const dataRows = Array.from({ length: 3 }, () => [
|
|
147
|
-
createCellData({ value: 'A' }, defaultCellFormat),
|
|
148
|
-
createCellData({ value: 'B' }, defaultCellFormat),
|
|
149
|
-
createCellData({ value: 'C' }, defaultCellFormat),
|
|
150
|
-
]);
|
|
151
|
-
await sheet.addRowData([headerRow], false);
|
|
152
|
-
await sheet.addRowData(dataRows, true);
|
|
153
|
-
|
|
154
|
-
// Apply all formatting at once
|
|
155
|
-
await sheet.frozen(1, 1);
|
|
156
|
-
await sheet.hideCol(2);
|
|
157
|
-
await sheet.conditionalFormat([0], {
|
|
158
|
-
booleanRule: {
|
|
159
|
-
condition: {
|
|
160
|
-
type: 'NUMBER_GREATER_THAN_EQ',
|
|
161
|
-
values: [{ userEnteredValue: '400' }],
|
|
162
|
-
},
|
|
163
|
-
format: {
|
|
164
|
-
backgroundColor: { red: 0.9 },
|
|
165
|
-
textFormat: {
|
|
166
|
-
foregroundColor: { red: 1, green: 1, blue: 1 },
|
|
167
|
-
},
|
|
168
|
-
},
|
|
169
|
-
},
|
|
170
|
-
});
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
afterAll(async () => {
|
|
174
|
-
try {
|
|
175
|
-
await deleteSheet(auth, SPREADSHEET_ID, sheetId);
|
|
176
|
-
} catch {
|
|
177
|
-
// ignore
|
|
178
|
-
}
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
it('frozen() で行列が固定されている', async () => {
|
|
182
|
-
const sheetData = await getSheetProperties(auth, SPREADSHEET_ID, sheetId);
|
|
183
|
-
expect(sheetData?.properties?.gridProperties?.frozenColumnCount).toBe(1);
|
|
184
|
-
expect(sheetData?.properties?.gridProperties?.frozenRowCount).toBe(1);
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
it('hideCol() で列が非表示になっている', async () => {
|
|
188
|
-
const sheetsApi = google.sheets({ version: 'v4', auth: auth as unknown });
|
|
189
|
-
const res = await sheetsApi.spreadsheets.get({
|
|
190
|
-
spreadsheetId: SPREADSHEET_ID,
|
|
191
|
-
includeGridData: false,
|
|
192
|
-
fields: 'sheets(properties,data.columnMetadata)',
|
|
193
|
-
ranges: [`'${sheetName}'`],
|
|
194
|
-
});
|
|
195
|
-
const targetSheet = res.data.sheets?.[0];
|
|
196
|
-
const colMetadata = targetSheet?.data?.[0]?.columnMetadata;
|
|
197
|
-
expect(colMetadata?.[2]?.hiddenByUser).toBe(true);
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
it('conditionalFormat() で条件付き書式が設定されている', async () => {
|
|
201
|
-
const sheetsApi = google.sheets({ version: 'v4', auth: auth as unknown });
|
|
202
|
-
const res = await sheetsApi.spreadsheets.get({
|
|
203
|
-
spreadsheetId: SPREADSHEET_ID,
|
|
204
|
-
includeGridData: false,
|
|
205
|
-
fields: 'sheets(properties,conditionalFormats)',
|
|
206
|
-
});
|
|
207
|
-
const targetSheet = res.data.sheets?.find((s) => s.properties?.sheetId === sheetId);
|
|
208
|
-
const formats = targetSheet?.conditionalFormats;
|
|
209
|
-
expect(formats).toBeDefined();
|
|
210
|
-
expect(formats!.length).toBeGreaterThanOrEqual(1);
|
|
211
|
-
|
|
212
|
-
const rule = formats![0]!;
|
|
213
|
-
expect(rule.booleanRule?.condition?.type).toBe('NUMBER_GREATER_THAN_EQ');
|
|
214
|
-
expect(rule.ranges?.[0]?.startColumnIndex).toBe(0);
|
|
215
|
-
expect(rule.ranges?.[0]?.endColumnIndex).toBe(1);
|
|
216
|
-
});
|
|
217
|
-
});
|
package/src/archive.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { Archive } from '@nitpicker/crawler';
|
|
2
|
-
|
|
3
|
-
import { archiveLog } from './debug.js';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
*
|
|
7
|
-
* @param filePath
|
|
8
|
-
*/
|
|
9
|
-
export async function getArchive(filePath: string) {
|
|
10
|
-
archiveLog('Open file: %s', filePath);
|
|
11
|
-
const archive = await Archive.open({
|
|
12
|
-
filePath,
|
|
13
|
-
openPluginData: true,
|
|
14
|
-
});
|
|
15
|
-
archiveLog('File open succeeded');
|
|
16
|
-
|
|
17
|
-
const close = async () => {
|
|
18
|
-
await archive.close();
|
|
19
|
-
process.exit();
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
archiveLog('Bind close method to SIGINT, SIGBREAK, SIGHUP, SIGABRT events');
|
|
23
|
-
process.on('SIGINT', close);
|
|
24
|
-
process.on('SIGBREAK', close);
|
|
25
|
-
process.on('SIGHUP', close);
|
|
26
|
-
process.on('SIGABRT', close);
|
|
27
|
-
|
|
28
|
-
return archive;
|
|
29
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Placeholder for the "Summary" sheet generation.
|
|
3
|
-
*
|
|
4
|
-
* Intended to aggregate key metrics from all other sheets into a
|
|
5
|
-
* single overview tab. Not yet implemented; calling this function
|
|
6
|
-
* is currently a no-op.
|
|
7
|
-
*/
|
|
8
|
-
export async function addToSummary() {
|
|
9
|
-
// Not yet implemented
|
|
10
|
-
}
|