83 lines
4.5 KiB
JavaScript
83 lines
4.5 KiB
JavaScript
import {BackendClient} from '../../../src/lib/sccn/api';
|
|
|
|
const response = (data, status = 200) => ({ok: status < 400, status, json: async () => data});
|
|
|
|
describe('ScCN backend adapter', () => {
|
|
let client;
|
|
beforeEach(() => {
|
|
client = new BackendClient();
|
|
global.fetch = jest.fn();
|
|
});
|
|
afterEach(() => { delete global.fetch; });
|
|
|
|
test('uploads even clean builtin assets once per server session', async () => {
|
|
fetch.mockResolvedValue(response({status: 'ok'}));
|
|
const asset = {assetId: 'abc', dataFormat: 'svg', data: new Uint8Array([1, 2]), clean: true};
|
|
await client.upload(asset);
|
|
await client.upload(asset);
|
|
expect(fetch).toHaveBeenCalledTimes(1);
|
|
expect(fetch.mock.calls[0][1]).toMatchObject({credentials: 'include', method: 'POST', body: asset.data});
|
|
});
|
|
test('failed uploads remain retryable', async () => {
|
|
fetch.mockResolvedValueOnce(response({code: 40101}, 401)).mockResolvedValue(response({status: 'ok'}));
|
|
const asset = {assetId: 'abc', dataFormat: 'svg', data: new Uint8Array([1])};
|
|
await expect(client.upload(asset)).rejects.toThrow('登录');
|
|
await client.upload(asset);
|
|
expect(fetch).toHaveBeenCalledTimes(2);
|
|
});
|
|
test('creates a project with raw JSON and updates with the loaded revision', async () => {
|
|
fetch.mockResolvedValueOnce(response({'content-name': '12', id: 12, revision: 1}))
|
|
.mockResolvedValueOnce(response({id: 12, revision: 2}));
|
|
await client.save(null, '{"targets":[]}', {title: '标题'});
|
|
await client.save('12', '{"targets":[]}', {title: '新标题'});
|
|
expect(fetch.mock.calls[0][0]).toContain('/editor/projects/?title=');
|
|
expect(fetch.mock.calls[0][1].body).toBe('{"targets":[]}');
|
|
expect(fetch.mock.calls[1][0]).toContain('revision=1');
|
|
expect(client.revisions.get('12')).toBe(2);
|
|
});
|
|
test('does not overwrite the loaded revision when refreshing permissions', async () => {
|
|
fetch.mockResolvedValueOnce(response({data: {revision: 4, canSave: true}}))
|
|
.mockResolvedValueOnce(response({data: {revision: 9, canSave: true}}))
|
|
.mockResolvedValueOnce(response({code: 40902}, 409));
|
|
await client.project('12');
|
|
await client.permissions('12');
|
|
expect(client.revisions.get('12')).toBe(4);
|
|
await expect(client.save('12', '{}')).rejects.toThrow('其他窗口');
|
|
expect(client.revisions.get('12')).toBe(4);
|
|
expect(fetch.mock.calls[2][0]).toContain('revision=4');
|
|
});
|
|
test('copy and remix create new IDs without a revision precondition', async () => {
|
|
fetch.mockResolvedValue(response({id: 13, revision: 1}));
|
|
await client.save(null, '{}', {originalId: 12, isCopy: 1});
|
|
expect(fetch.mock.calls[0][0]).toContain('original_id=12&is_copy=1');
|
|
expect(fetch.mock.calls[0][0]).not.toContain('revision=');
|
|
await client.save(null, '{}', {originalId: 12, isRemix: 1});
|
|
expect(fetch.mock.calls[1][0]).toContain('original_id=12&is_remix=1');
|
|
});
|
|
test('refuses updates without a loaded version', async () => {
|
|
await expect(client.save('12', '{}')).rejects.toThrow('版本');
|
|
expect(fetch).not.toHaveBeenCalled();
|
|
});
|
|
test('switching accounts clears cached ownership', async () => {
|
|
client.state = {...client.state, user: {id: 1}, projects: {12: {canSave: true}}};
|
|
fetch.mockResolvedValue(response({data: {user: {id: 2}}}));
|
|
await client.session();
|
|
expect(client.state.projects).toEqual({});
|
|
expect(client.state.user.id).toBe(2);
|
|
});
|
|
test('logout clears ownership and uses the HttpOnly cookie', async () => {
|
|
client.state = {...client.state, user: {id: 1}, projects: {12: {canSave: true}}};
|
|
fetch.mockResolvedValue(response({code: 0}));
|
|
await client.logout();
|
|
expect(client.state.user).toBe(null);
|
|
expect(client.state.projects).toEqual({});
|
|
expect(fetch.mock.calls[0][1]).toMatchObject({credentials: 'include', method: 'POST'});
|
|
});
|
|
test('handles network and non-JSON responses without claiming a successful save', async () => {
|
|
fetch.mockRejectedValueOnce(new Error('network'));
|
|
await expect(client.save(null, '{}')).rejects.toThrow('无法连接服务器');
|
|
fetch.mockResolvedValueOnce({ok: true, status: 200, json: async () => { throw new Error('html'); }});
|
|
await expect(client.save(null, '{}')).rejects.toThrow('无效响应');
|
|
});
|
|
});
|