microsoft-onedrive-mock 1.0.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.
- package/.ENV_EXAMPLE +16 -0
- package/.github/workflows/ci.yml +41 -0
- package/.github/workflows/release.yml +50 -0
- package/README.md +1 -0
- package/dist/batch.d.ts +2 -0
- package/dist/batch.js +93 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +115 -0
- package/dist/routes/v1.d.ts +1 -0
- package/dist/routes/v1.js +201 -0
- package/dist/store.d.ts +24 -0
- package/dist/store.js +175 -0
- package/dist/types.d.ts +60 -0
- package/dist/types.js +2 -0
- package/eslint.config.mjs +15 -0
- package/examples/device-login.ts +84 -0
- package/examples/microsoft-login.html +227 -0
- package/examples/serve-login.ts +11 -0
- package/package.json +48 -0
- package/scripts/check-token.ts +70 -0
- package/specs/odata.xml +2 -0
- package/specs/openapi.yaml +196491 -0
- package/src/batch.ts +91 -0
- package/src/index.ts +102 -0
- package/src/routes/v1.ts +227 -0
- package/src/store.ts +191 -0
- package/src/types.ts +59 -0
- package/test/basics.test.ts +119 -0
- package/test/batch.test.ts +75 -0
- package/test/config.ts +63 -0
- package/test/etag.test.ts +69 -0
- package/test/search.test.ts +57 -0
- package/test/select.test.ts +68 -0
- package/tsconfig.json +19 -0
- package/vitest.config.ts +33 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import { describe, it, expect, beforeAll, afterAll, beforeEach } from 'vitest';
|
|
3
|
+
import { setupTestEnvironment } from './config';
|
|
4
|
+
|
|
5
|
+
describe('OneDrive Basics', () => {
|
|
6
|
+
let env: Awaited<ReturnType<typeof setupTestEnvironment>>;
|
|
7
|
+
let baseUrl: string;
|
|
8
|
+
let token: string;
|
|
9
|
+
let headers: Record<string, string>;
|
|
10
|
+
|
|
11
|
+
beforeAll(async () => {
|
|
12
|
+
env = await setupTestEnvironment();
|
|
13
|
+
baseUrl = env.baseUrl;
|
|
14
|
+
token = env.token;
|
|
15
|
+
headers = { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' };
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
afterAll(async () => {
|
|
19
|
+
if (env) env.close();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
beforeEach(async () => {
|
|
23
|
+
await env.clear();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('should get drive root', async () => {
|
|
27
|
+
const res = await fetch(`${baseUrl}/v1.0/me/drive/root`, { headers });
|
|
28
|
+
if (res.status !== 200) console.log("ROOT ERROR:", await res.text());
|
|
29
|
+
expect(res.status).toBe(200);
|
|
30
|
+
const data = await res.json();
|
|
31
|
+
expect(data.id).toBeDefined();
|
|
32
|
+
expect(data.name).toBe('root');
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('should create a new folder and verify it appears in children', async () => {
|
|
36
|
+
const folderName = 'TestFolder_' + Date.now();
|
|
37
|
+
const folderRes = await fetch(`${baseUrl}/v1.0/me/drive/items/root/children`, {
|
|
38
|
+
method: 'POST',
|
|
39
|
+
headers,
|
|
40
|
+
body: JSON.stringify({
|
|
41
|
+
name: folderName,
|
|
42
|
+
folder: {},
|
|
43
|
+
'@microsoft.graph.conflictBehavior': 'rename'
|
|
44
|
+
})
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
if (folderRes.status !== 201) console.log("FOLDER ERROR:", await folderRes.text());
|
|
48
|
+
expect(folderRes.status).toBe(201);
|
|
49
|
+
const folderData = await folderRes.json();
|
|
50
|
+
expect(folderData.name).toBe(folderName);
|
|
51
|
+
expect(folderData.id).toBeDefined();
|
|
52
|
+
|
|
53
|
+
// List children
|
|
54
|
+
const listRes = await fetch(`${baseUrl}/v1.0/me/drive/items/root/children`, { headers });
|
|
55
|
+
const listData = await listRes.json();
|
|
56
|
+
expect(listData.value).toBeDefined();
|
|
57
|
+
expect(listData.value.find((i: any) => i.id === folderData.id)).toBeDefined();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('should upload a new file via PUT and download it via GET', async () => {
|
|
61
|
+
const filename = 'test-file-' + Date.now() + '.txt';
|
|
62
|
+
const content = 'Hello World One Drive!';
|
|
63
|
+
|
|
64
|
+
const putRes = await fetch(`${baseUrl}/v1.0/me/drive/items/root:/${filename}:/content`, {
|
|
65
|
+
method: 'PUT',
|
|
66
|
+
headers: {
|
|
67
|
+
...headers,
|
|
68
|
+
'Content-Type': 'text/plain'
|
|
69
|
+
},
|
|
70
|
+
body: content
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
if (putRes.status !== 201) console.log("PUT ERROR:", await putRes.text());
|
|
74
|
+
expect(putRes.status).toBe(201); // 201 Created for new file
|
|
75
|
+
const fileData = await putRes.json();
|
|
76
|
+
expect(fileData.id).toBeDefined();
|
|
77
|
+
expect(fileData.name).toBe(filename);
|
|
78
|
+
|
|
79
|
+
// Upload again to same path should update (200 OK)
|
|
80
|
+
const putRes2 = await fetch(`${baseUrl}/v1.0/me/drive/items/root:/${filename}:/content`, {
|
|
81
|
+
method: 'PUT',
|
|
82
|
+
headers: {
|
|
83
|
+
...headers,
|
|
84
|
+
'Content-Type': 'text/plain'
|
|
85
|
+
},
|
|
86
|
+
body: 'Updated Content'
|
|
87
|
+
});
|
|
88
|
+
expect(putRes2.status).toBe(200);
|
|
89
|
+
|
|
90
|
+
// Download content via item id
|
|
91
|
+
const dlRes = await fetch(`${baseUrl}/v1.0/me/drive/items/${fileData.id}/content`, { headers });
|
|
92
|
+
expect(dlRes.status).toBe(200);
|
|
93
|
+
const dlText = await dlRes.text();
|
|
94
|
+
expect(dlText).toBe('Updated Content');
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('should handle delta queries', async () => {
|
|
98
|
+
const res1 = await fetch(`${baseUrl}/v1.0/me/drive/root/delta`, { headers });
|
|
99
|
+
const delta1 = await res1.json();
|
|
100
|
+
expect(delta1.value).toBeDefined();
|
|
101
|
+
expect(delta1['@odata.deltaLink']).toBeDefined();
|
|
102
|
+
|
|
103
|
+
const deltaFilename = 'delta-file-' + Date.now() + '.txt';
|
|
104
|
+
|
|
105
|
+
// Create a file
|
|
106
|
+
await fetch(`${baseUrl}/v1.0/me/drive/items/root:/${deltaFilename}:/content`, {
|
|
107
|
+
method: 'PUT',
|
|
108
|
+
headers: { ...headers, 'Content-Type': 'text/plain' },
|
|
109
|
+
body: 'delta'
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
const tokenUrl = delta1['@odata.deltaLink'];
|
|
113
|
+
|
|
114
|
+
const res2 = await fetch(tokenUrl, { headers });
|
|
115
|
+
const delta2 = await res2.json();
|
|
116
|
+
expect(delta2.value.length).toBeGreaterThan(0);
|
|
117
|
+
expect(delta2.value.find((i: any) => i.name === deltaFilename)).toBeDefined();
|
|
118
|
+
});
|
|
119
|
+
});
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import { describe, it, expect, beforeAll, afterAll, beforeEach } from 'vitest';
|
|
3
|
+
import { setupTestEnvironment } from './config';
|
|
4
|
+
|
|
5
|
+
describe('OneDrive Batch Requests', () => {
|
|
6
|
+
let env: Awaited<ReturnType<typeof setupTestEnvironment>>;
|
|
7
|
+
let baseUrl: string;
|
|
8
|
+
let token: string;
|
|
9
|
+
let headers: Record<string, string>;
|
|
10
|
+
|
|
11
|
+
beforeAll(async () => {
|
|
12
|
+
env = await setupTestEnvironment();
|
|
13
|
+
baseUrl = env.baseUrl;
|
|
14
|
+
token = env.token;
|
|
15
|
+
headers = { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' };
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
afterAll(async () => {
|
|
19
|
+
if (env) env.close();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
beforeEach(async () => {
|
|
23
|
+
await env.clear();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('should execute a batch of requests', async () => {
|
|
27
|
+
const batchFolderName = 'BatchFolder_' + Date.now();
|
|
28
|
+
const batchPayload = {
|
|
29
|
+
requests: [
|
|
30
|
+
{
|
|
31
|
+
id: "1",
|
|
32
|
+
method: "GET",
|
|
33
|
+
url: "/me/drive/root"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
id: "2",
|
|
37
|
+
method: "POST",
|
|
38
|
+
url: "/me/drive/items/root/children",
|
|
39
|
+
headers: {
|
|
40
|
+
"Content-Type": "application/json"
|
|
41
|
+
},
|
|
42
|
+
body: {
|
|
43
|
+
name: batchFolderName,
|
|
44
|
+
folder: {}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
]
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const res = await fetch(`${baseUrl}/v1.0/$batch`, {
|
|
51
|
+
method: 'POST',
|
|
52
|
+
headers,
|
|
53
|
+
body: JSON.stringify(batchPayload)
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
if (res.status !== 200) console.log("BATCH ERROR:", await res.text());
|
|
57
|
+
expect(res.status).toBe(200);
|
|
58
|
+
const data = await res.json();
|
|
59
|
+
|
|
60
|
+
expect(data.responses).toBeDefined();
|
|
61
|
+
expect(Array.isArray(data.responses)).toBe(true);
|
|
62
|
+
expect(data.responses.length).toBe(2);
|
|
63
|
+
|
|
64
|
+
const res1 = data.responses.find((r: any) => r.id === "1");
|
|
65
|
+
const res2 = data.responses.find((r: any) => r.id === "2");
|
|
66
|
+
|
|
67
|
+
expect(res1.status).toBe(200);
|
|
68
|
+
expect(res1.body.id).toBeDefined();
|
|
69
|
+
expect(res1.body.name).toBe('root');
|
|
70
|
+
|
|
71
|
+
expect(res2.status).toBe(201);
|
|
72
|
+
expect(res2.body.name).toBe(batchFolderName);
|
|
73
|
+
expect(res2.body.folder).toBeDefined();
|
|
74
|
+
});
|
|
75
|
+
});
|
package/test/config.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// Note: We avoid static imports of node-only modules to support browser mode.
|
|
2
|
+
// Types are fine.
|
|
3
|
+
|
|
4
|
+
export const getAuthToken = () => {
|
|
5
|
+
if (typeof process !== 'undefined' && process.env.TEST_TARGET === 'real') {
|
|
6
|
+
const token = process.env.ONEDRIVE_TOKEN || process.env.GRAPH_TOKEN;
|
|
7
|
+
if (!token) throw new Error("Real API test requires ONEDRIVE_TOKEN or GRAPH_TOKEN in env");
|
|
8
|
+
return token;
|
|
9
|
+
}
|
|
10
|
+
return 'valid-token';
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const setupTestEnvironment = async () => {
|
|
14
|
+
const isBrowser = typeof window !== 'undefined';
|
|
15
|
+
const isReal = typeof process !== 'undefined' && process.env.TEST_TARGET === 'real';
|
|
16
|
+
|
|
17
|
+
if (isReal) {
|
|
18
|
+
console.log("Running tests against REAL Microsoft Graph API");
|
|
19
|
+
return {
|
|
20
|
+
baseUrl: 'https://graph.microsoft.com',
|
|
21
|
+
token: getAuthToken(),
|
|
22
|
+
close: () => { },
|
|
23
|
+
clear: async () => { } // Real API clear not implemented
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (isBrowser) {
|
|
28
|
+
console.log("Running tests against MOCK Server (Browser)");
|
|
29
|
+
const serverUrl = 'http://localhost:3006';
|
|
30
|
+
return {
|
|
31
|
+
baseUrl: serverUrl,
|
|
32
|
+
token: 'valid-token',
|
|
33
|
+
close: () => { },
|
|
34
|
+
clear: async () => {
|
|
35
|
+
await fetch(`${serverUrl}/debug/clear`, { method: 'POST' });
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
} else {
|
|
39
|
+
console.log("Running tests against MOCK Server (Node)");
|
|
40
|
+
const { startServer } = await import('../src/index');
|
|
41
|
+
const { driveStore } = await import('../src/store');
|
|
42
|
+
|
|
43
|
+
const server = startServer(0, 'localhost', { serverLagBefore: 5, serverLagAfter: 5 });
|
|
44
|
+
|
|
45
|
+
await new Promise<void>((resolve) => {
|
|
46
|
+
if (server.listening) return resolve();
|
|
47
|
+
server.on('listening', resolve);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const addr = server.address();
|
|
51
|
+
const port = typeof addr === 'object' && addr && 'port' in addr ? addr.port : 0;
|
|
52
|
+
const baseUrl = `http://localhost:${port}`;
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
baseUrl,
|
|
56
|
+
token: 'valid-token',
|
|
57
|
+
close: () => server.close(),
|
|
58
|
+
clear: async () => {
|
|
59
|
+
driveStore.clear();
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { describe, it, expect, beforeAll, afterAll, beforeEach } from 'vitest';
|
|
2
|
+
import { setupTestEnvironment } from './config';
|
|
3
|
+
|
|
4
|
+
describe('ETag and Conditional Operations (If-Match)', () => {
|
|
5
|
+
let env: Awaited<ReturnType<typeof setupTestEnvironment>>;
|
|
6
|
+
let baseUrl: string;
|
|
7
|
+
let token: string;
|
|
8
|
+
let headers: Record<string, string>;
|
|
9
|
+
|
|
10
|
+
beforeAll(async () => {
|
|
11
|
+
env = await setupTestEnvironment();
|
|
12
|
+
baseUrl = env.baseUrl;
|
|
13
|
+
token = env.token;
|
|
14
|
+
headers = { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' };
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
afterAll(async () => {
|
|
18
|
+
if (env) env.close();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
beforeEach(async () => {
|
|
22
|
+
await env.clear();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('should support If-Match on PUT content update', async () => {
|
|
26
|
+
const filename = 'etag-test-' + Date.now() + '.txt';
|
|
27
|
+
|
|
28
|
+
// 1. Create file
|
|
29
|
+
const putRes = await fetch(`${baseUrl}/v1.0/me/drive/items/root:/${filename}:/content`, {
|
|
30
|
+
method: 'PUT',
|
|
31
|
+
headers: { ...headers, 'Content-Type': 'text/plain' },
|
|
32
|
+
body: 'Initial content'
|
|
33
|
+
});
|
|
34
|
+
expect(putRes.status).toBe(201);
|
|
35
|
+
const fileData = await putRes.json();
|
|
36
|
+
const etag = fileData.eTag;
|
|
37
|
+
const fileId = fileData.id;
|
|
38
|
+
expect(etag).toBeDefined();
|
|
39
|
+
|
|
40
|
+
// 2. Update with Wrong ETag
|
|
41
|
+
const invalidEtag = etag.replace(/.$/, '0'); // Change last char
|
|
42
|
+
const updateFail = await fetch(`${baseUrl}/v1.0/me/drive/items/${fileId}/content`, {
|
|
43
|
+
method: 'PUT',
|
|
44
|
+
headers: { ...headers, 'Content-Type': 'text/plain', 'If-Match': invalidEtag },
|
|
45
|
+
body: 'Should fail'
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// Mock vs Real Parity verification
|
|
49
|
+
// Graph API uses 412 (Precondition Failed) when ETags do not match
|
|
50
|
+
expect(updateFail.status).toBe(412);
|
|
51
|
+
|
|
52
|
+
// Verify content did not change
|
|
53
|
+
const checkRes = await fetch(`${baseUrl}/v1.0/me/drive/items/${fileId}/content`, { headers });
|
|
54
|
+
expect(await checkRes.text()).toBe('Initial content');
|
|
55
|
+
|
|
56
|
+
// 3. Update with Correct ETag
|
|
57
|
+
const updateSuccess = await fetch(`${baseUrl}/v1.0/me/drive/items/${fileId}/content`, {
|
|
58
|
+
method: 'PUT',
|
|
59
|
+
headers: { ...headers, 'Content-Type': 'text/plain', 'If-Match': etag },
|
|
60
|
+
body: 'Updated content'
|
|
61
|
+
});
|
|
62
|
+
expect(updateSuccess.status).toBe(200);
|
|
63
|
+
|
|
64
|
+
// 4. Verify new ETag is different
|
|
65
|
+
const newFileData = await updateSuccess.json();
|
|
66
|
+
expect(newFileData.eTag).toBeDefined();
|
|
67
|
+
expect(newFileData.eTag).not.toBe(etag);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { describe, it, expect, beforeAll, afterAll, beforeEach } from 'vitest';
|
|
2
|
+
import { setupTestEnvironment } from './config';
|
|
3
|
+
|
|
4
|
+
describe('Folder Search Parity', () => {
|
|
5
|
+
let env: Awaited<ReturnType<typeof setupTestEnvironment>>;
|
|
6
|
+
let baseUrl: string;
|
|
7
|
+
let token: string;
|
|
8
|
+
let headers: Record<string, string>;
|
|
9
|
+
|
|
10
|
+
beforeAll(async () => {
|
|
11
|
+
env = await setupTestEnvironment();
|
|
12
|
+
baseUrl = env.baseUrl;
|
|
13
|
+
token = env.token;
|
|
14
|
+
headers = { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' };
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
afterAll(async () => {
|
|
18
|
+
if (env) env.close();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
beforeEach(async () => {
|
|
22
|
+
await env.clear();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('should validate search functionality (empty string handling)', async () => {
|
|
26
|
+
// Due to project guidelines requiring < 10s test timeouts, we cannot wait for Microsoft Graph
|
|
27
|
+
// to index newly created files, which natively takes 15-60 seconds.
|
|
28
|
+
// Instead, we validate that the search endpoint correctly routes, authenticates,
|
|
29
|
+
// and returns the exact identical array shape for a guaranteed empty query.
|
|
30
|
+
const uniqueString = Date.now().toString();
|
|
31
|
+
const searchName = 'NonExistent_' + uniqueString;
|
|
32
|
+
|
|
33
|
+
const searchRes = await fetch(`${baseUrl}/v1.0/me/drive/root/search(q='${searchName}')`, { headers });
|
|
34
|
+
expect(searchRes.status).toBe(200);
|
|
35
|
+
|
|
36
|
+
const searchData = await searchRes.json();
|
|
37
|
+
expect(searchData.value).toBeDefined();
|
|
38
|
+
|
|
39
|
+
// Both Mock and Real API should instantly return an empty array
|
|
40
|
+
expect(Array.isArray(searchData.value)).toBe(true);
|
|
41
|
+
expect(searchData.value.length).toBe(0);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('should respect $select fields during search routing', async () => {
|
|
45
|
+
const searchName = 'SelectSearchTarget_' + Date.now().toString();
|
|
46
|
+
|
|
47
|
+
// Search for the uniquely missing file with ?$select=id
|
|
48
|
+
const searchRes = await fetch(`${baseUrl}/v1.0/me/drive/root/search(q='${searchName}')?$select=id`, { headers });
|
|
49
|
+
expect(searchRes.status).toBe(200);
|
|
50
|
+
|
|
51
|
+
const data = await searchRes.json();
|
|
52
|
+
|
|
53
|
+
expect(data.value).toBeDefined();
|
|
54
|
+
expect(Array.isArray(data.value)).toBe(true);
|
|
55
|
+
expect(data.value.length).toBe(0);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|
3
|
+
import { setupTestEnvironment } from './config';
|
|
4
|
+
|
|
5
|
+
describe('Field Selection Parity ($select)', () => {
|
|
6
|
+
let env: Awaited<ReturnType<typeof setupTestEnvironment>>;
|
|
7
|
+
let baseUrl: string;
|
|
8
|
+
let token: string;
|
|
9
|
+
let headers: Record<string, string>;
|
|
10
|
+
|
|
11
|
+
beforeAll(async () => {
|
|
12
|
+
env = await setupTestEnvironment();
|
|
13
|
+
baseUrl = env.baseUrl;
|
|
14
|
+
token = env.token;
|
|
15
|
+
headers = { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' };
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
afterAll(async () => {
|
|
19
|
+
if (env) env.close();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('should return only requested fields using $select on item get', async () => {
|
|
23
|
+
// 1. Get root, but only strictly its name and id (filtering out size, folder, parentReference, etc)
|
|
24
|
+
const res = await fetch(`${baseUrl}/v1.0/me/drive/root?$select=id,name`, { headers });
|
|
25
|
+
expect(res.status).toBe(200);
|
|
26
|
+
|
|
27
|
+
const data = await res.json();
|
|
28
|
+
|
|
29
|
+
// Ensure id and name are there
|
|
30
|
+
expect(data.id).toBeDefined();
|
|
31
|
+
expect(data.name).toBe('root');
|
|
32
|
+
|
|
33
|
+
// Ensure nothing else got leaked
|
|
34
|
+
// Ensure nothing else got leaked
|
|
35
|
+
// NOTE: Graph API might occasionally append OData annotations implicitly like @odata.context.
|
|
36
|
+
// We will assert the base structure without extra root keys like size or folder.
|
|
37
|
+
expect(data.size).toBeUndefined();
|
|
38
|
+
expect(data.folder).toBeUndefined();
|
|
39
|
+
expect(data.parentReference).toBeUndefined();
|
|
40
|
+
expect(data.fileSystemInfo).toBeUndefined();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('should return only requested fields in list arrays', async () => {
|
|
44
|
+
// Create a subfolder to exist in children
|
|
45
|
+
const folderName = 'SelectTestFolder_' + Date.now();
|
|
46
|
+
await fetch(`${baseUrl}/v1.0/me/drive/items/root/children`, {
|
|
47
|
+
method: 'POST',
|
|
48
|
+
headers,
|
|
49
|
+
body: JSON.stringify({ name: folderName, folder: {} })
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// 2. Get children with $select bounds
|
|
53
|
+
const res = await fetch(`${baseUrl}/v1.0/me/drive/items/root/children?$select=id,name`, { headers });
|
|
54
|
+
expect(res.status).toBe(200);
|
|
55
|
+
|
|
56
|
+
const data = await res.json();
|
|
57
|
+
expect(data.value).toBeDefined();
|
|
58
|
+
expect(data.value.length).toBeGreaterThan(0);
|
|
59
|
+
|
|
60
|
+
const testFolder = data.value.find((i: any) => i.name === folderName);
|
|
61
|
+
expect(testFolder).toBeDefined();
|
|
62
|
+
|
|
63
|
+
// Deeply check that testFolder inside the array respect limits
|
|
64
|
+
expect(testFolder.id).toBeDefined();
|
|
65
|
+
expect(testFolder.size).toBeUndefined();
|
|
66
|
+
expect(testFolder.folder).toBeUndefined();
|
|
67
|
+
});
|
|
68
|
+
});
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "es2016",
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"esModuleInterop": true,
|
|
6
|
+
"forceConsistentCasingInFileNames": true,
|
|
7
|
+
"strict": true,
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"outDir": "./dist",
|
|
10
|
+
"declaration": true
|
|
11
|
+
},
|
|
12
|
+
"include": [
|
|
13
|
+
"src/**/*"
|
|
14
|
+
],
|
|
15
|
+
"exclude": [
|
|
16
|
+
"node_modules",
|
|
17
|
+
"**/*.test.ts"
|
|
18
|
+
]
|
|
19
|
+
}
|
package/vitest.config.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { defineConfig } from 'vitest/config';
|
|
2
|
+
import { playwright } from '@vitest/browser-playwright';
|
|
3
|
+
import dotenv from 'dotenv';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
|
|
6
|
+
// Load .ENV file if it exists
|
|
7
|
+
dotenv.config({ path: path.resolve(__dirname, '.ENV') });
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
export default defineConfig({
|
|
11
|
+
define: {
|
|
12
|
+
'process.env.ONEDRIVE_TOKEN': JSON.stringify(process.env.ONEDRIVE_TOKEN),
|
|
13
|
+
'process.env.TEST_TARGET': JSON.stringify(process.env.TEST_TARGET),
|
|
14
|
+
'process.env.BROWSER_ENABLED': JSON.stringify(process.env.BROWSER_ENABLED),
|
|
15
|
+
},
|
|
16
|
+
test: {
|
|
17
|
+
env: {
|
|
18
|
+
// Inject these variables into the browser environment
|
|
19
|
+
ONEDRIVE_TOKEN: process.env.ONEDRIVE_TOKEN,
|
|
20
|
+
TEST_TARGET: process.env.TEST_TARGET,
|
|
21
|
+
BROWSER_ENABLED: process.env.BROWSER_ENABLED
|
|
22
|
+
},
|
|
23
|
+
browser: {
|
|
24
|
+
enabled: process.env.BROWSER_ENABLED === 'true',
|
|
25
|
+
provider: playwright(),
|
|
26
|
+
instances: [
|
|
27
|
+
{ browser: 'chromium' }
|
|
28
|
+
],
|
|
29
|
+
headless: true,
|
|
30
|
+
screenshotFailures: false,
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
});
|