Skip to content

Commit 0165b2d

Browse files
authored
Yarn dev debug (#602)
- improve dev exp on fresh clone - fix and add regression test against broken `serverRequest`
2 parents a201720 + 9593ed0 commit 0165b2d

File tree

4 files changed

+234
-24
lines changed

4 files changed

+234
-24
lines changed

packages/express/.env.development

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
COUCHDB_SERVER=fillthisin .env.development.local //localhost:5984/
2-
COUCHDB_PROTOCOL=fillthisin .env.develoment.local //http
3-
COUCHDB_ADMIN=admin .env.development.local //your couchdb admin username
4-
COUCHDB_PASSWORD=password .env.development.local //your couchdb admin pw
1+
# This config aligns with the `dev` script at the project root,
2+
# and assumes that the CouchDB backend is available
3+
4+
COUCHDB_SERVER=localhost:5984
5+
COUCHDB_PROTOCOL=http
6+
COUCHDB_ADMIN=admin
7+
COUCHDB_PASSWORD=password
58

69
VERSION=localdev

packages/express/src/utils/env.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
import dotenv from 'dotenv';
22
import process from 'process';
3+
import fs from 'fs';
34

45
dotenv.config({
5-
path: process.argv && process.argv.length == 3 ? process.argv[2] : '.env.development.local',
6+
path:
7+
process.argv && process.argv.length == 3
8+
? process.argv[2]
9+
: '.env.development',
610
});
711

812
type Env = {
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
import { vi, describe, test, expect, beforeEach } from 'vitest';
2+
import serverRequest from './index';
3+
import { ServerRequest, Status } from '@vue-skuilder/common';
4+
5+
// Create a minimal type that matches ServerRequest for testing
6+
type TestRequest = ServerRequest & {
7+
type: 'TEST_REQUEST';
8+
timeout?: number;
9+
};
10+
11+
describe('serverRequest', () => {
12+
let mockXHR: {
13+
open: ReturnType<typeof vi.fn>;
14+
send: ReturnType<typeof vi.fn>;
15+
setRequestHeader: ReturnType<typeof vi.fn>;
16+
withCredentials: boolean;
17+
onload?: () => void;
18+
ontimeout?: () => void;
19+
onerror?: () => void;
20+
readyState?: number;
21+
status?: number;
22+
responseText?: string;
23+
};
24+
25+
beforeEach(() => {
26+
mockXHR = {
27+
open: vi.fn(),
28+
send: vi.fn(),
29+
setRequestHeader: vi.fn(),
30+
withCredentials: false,
31+
};
32+
33+
global.XMLHttpRequest = vi.fn(() => mockXHR) as any;
34+
});
35+
36+
test('configures XMLHttpRequest correctly', async () => {
37+
// Set up the mock to do nothing on send
38+
mockXHR.send = vi.fn();
39+
40+
// Create a promise that resolves when onload is called
41+
const responsePromise = new Promise<void>((resolve) => {
42+
mockXHR.onload = () => resolve();
43+
});
44+
45+
// Start the request but don't wait for it
46+
const requestPromise = serverRequest<TestRequest>({ type: 'TEST_REQUEST' });
47+
48+
// Check that XMLHttpRequest was configured correctly
49+
expect(mockXHR.open).toHaveBeenCalledWith('POST', expect.any(String), true);
50+
expect(mockXHR.withCredentials).toBe(true);
51+
expect(mockXHR.setRequestHeader).toHaveBeenCalledWith('Content-Type', 'application/json');
52+
expect(mockXHR.timeout).toBe(7000);
53+
expect(mockXHR.send).toHaveBeenCalledWith(JSON.stringify({ type: 'TEST_REQUEST' }));
54+
55+
// Simulate successful response after validation
56+
mockXHR.responseText = JSON.stringify({ data: 'test', ok: true });
57+
mockXHR.onload && mockXHR.onload();
58+
59+
// Wait for the request to complete
60+
const result = await requestPromise;
61+
62+
// Verify the result
63+
expect(result.response).toEqual({ data: 'test', ok: true });
64+
});
65+
66+
test('handles successful response', async () => {
67+
const responseData = { status: Status.success, ok: true, data: { id: '123' } };
68+
69+
// Create a promise that will complete when onload is triggered
70+
const requestPromise = serverRequest<TestRequest>({ type: 'TEST_REQUEST' });
71+
72+
// Simulate a successful response
73+
mockXHR.responseText = JSON.stringify(responseData);
74+
mockXHR.onload && mockXHR.onload();
75+
76+
// Wait for the request to complete
77+
const result = await requestPromise;
78+
79+
// Verify the response was processed correctly
80+
expect(result.response).toEqual(responseData);
81+
});
82+
83+
test('handles JSON parse error in response', async () => {
84+
// Create a promise that will complete when onload is triggered
85+
const requestPromise = serverRequest<TestRequest>({ type: 'TEST_REQUEST' });
86+
87+
// Simulate an invalid JSON response
88+
mockXHR.responseText = 'Not valid JSON';
89+
mockXHR.onload && mockXHR.onload();
90+
91+
// Wait for the request to complete
92+
const result = await requestPromise;
93+
94+
// Verify error handling
95+
expect(result.response).toEqual({
96+
status: Status.error,
97+
ok: false,
98+
errorText: expect.stringContaining('Failed to parse response'),
99+
});
100+
});
101+
102+
test('handles request timeout', async () => {
103+
// Create a request with custom timeout
104+
const requestPromise = serverRequest<TestRequest>({
105+
type: 'TEST_REQUEST',
106+
timeout: 5000,
107+
});
108+
109+
// Verify timeout is set correctly
110+
expect(mockXHR.timeout).toBe(5000);
111+
112+
// Simulate timeout
113+
mockXHR.ontimeout && mockXHR.ontimeout();
114+
115+
// Wait for the request to complete
116+
const result = await requestPromise;
117+
118+
// Verify timeout handling
119+
expect(result.response).toEqual({
120+
status: Status.error,
121+
ok: false,
122+
errorText: 'Request timed out',
123+
});
124+
});
125+
126+
test('handles network error', async () => {
127+
// Create a request
128+
const requestPromise = serverRequest<TestRequest>({ type: 'TEST_REQUEST' });
129+
130+
// Simulate network error
131+
mockXHR.onerror && mockXHR.onerror();
132+
133+
// Wait for the request to complete
134+
const result = await requestPromise;
135+
136+
// Verify error handling
137+
expect(result.response).toEqual({
138+
status: Status.error,
139+
ok: false,
140+
errorText: 'Network error occurred',
141+
});
142+
});
143+
144+
test('handles exception during request setup', async () => {
145+
// Make XMLHttpRequest.open throw an error
146+
mockXHR.open = vi.fn().mockImplementation(() => {
147+
throw new Error('Connection refused');
148+
});
149+
150+
// Make the request which should catch the error
151+
const result = await serverRequest<TestRequest>({ type: 'TEST_REQUEST' });
152+
153+
// Verify error is caught and handled
154+
expect(result.response).toEqual({
155+
status: Status.error,
156+
ok: false,
157+
errorText: 'Connection refused',
158+
});
159+
});
160+
});

packages/vue/src/server/index.ts

Lines changed: 62 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,68 @@ import { ServerRequest } from '@vue-skuilder/common';
44

55
const SERVER = ENV.EXPRESS_SERVER_PROTOCOL + '://' + ENV.EXPRESS_SERVER_URL;
66

7+
/**
8+
* Makes an authenticated request to the express backend and returns the response.
9+
10+
* @param requestData
11+
* @returns
12+
*/
713
export default async function serverRequest<T extends ServerRequest>(requestData: T): Promise<T> {
8-
try {
9-
const xml = new XMLHttpRequest();
10-
xml.withCredentials = true;
11-
xml.open('POST', SERVER, false);
12-
xml.setRequestHeader('Content-Type', 'application/json');
13-
xml.timeout = requestData.timeout || 7000;
14-
xml.ontimeout = () => {
15-
throw new Error('Request timed out');
16-
};
17-
xml.send(JSON.stringify(requestData));
14+
return new Promise<T>((resolve) => {
15+
try {
16+
const xml = new XMLHttpRequest();
17+
xml.withCredentials = true;
18+
xml.open('POST', SERVER, true);
19+
xml.setRequestHeader('Content-Type', 'application/json');
20+
xml.timeout = requestData.timeout || 7000;
21+
22+
// Handle the response when it completes
23+
xml.onload = function () {
24+
try {
25+
requestData.response = JSON.parse(xml.responseText);
26+
resolve(requestData);
27+
} catch (parseError) {
28+
requestData.response = {
29+
status: Status.error,
30+
ok: false,
31+
errorText: `Failed to parse response: ${
32+
parseError instanceof Error ? parseError.message : JSON.stringify(parseError)
33+
}`,
34+
};
35+
resolve(requestData);
36+
}
37+
};
38+
39+
// Handle network errors
40+
xml.onerror = function () {
41+
requestData.response = {
42+
status: Status.error,
43+
ok: false,
44+
errorText: 'Network error occurred',
45+
};
46+
resolve(requestData);
47+
};
48+
49+
// Handle timeouts
50+
xml.ontimeout = function () {
51+
requestData.response = {
52+
status: Status.error,
53+
ok: false,
54+
errorText: 'Request timed out',
55+
};
56+
resolve(requestData);
57+
};
1858

19-
requestData.response = JSON.parse(xml.response);
20-
} catch (error) {
21-
requestData.response = {
22-
status: Status.error,
23-
ok: false,
24-
errorText: error instanceof Error ? error.message : JSON.stringify(error),
25-
};
26-
}
27-
return requestData;
59+
// Send the request
60+
xml.send(JSON.stringify(requestData));
61+
} catch (error) {
62+
// Handle any errors that occur during setup
63+
requestData.response = {
64+
status: Status.error,
65+
ok: false,
66+
errorText: error instanceof Error ? error.message : JSON.stringify(error),
67+
};
68+
resolve(requestData);
69+
}
70+
});
2871
}

0 commit comments

Comments
 (0)