Skip to content

Commit

Permalink
refactor: return Response from response utilities
Browse files Browse the repository at this point in the history
This also removes the dependency on async_hooks.
  • Loading branch information
smeijer committed Sep 17, 2021
1 parent 5ae488e commit f1a5e32
Show file tree
Hide file tree
Showing 8 changed files with 329 additions and 186 deletions.
23 changes: 22 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"@types/busboy": "^0.2.4",
"@types/cookies": "^0.7.7",
"@types/jest": "^27.0.1",
"@types/node-fetch": "^2.5.12",
"@types/react": "^17.0.20",
"@typescript-eslint/eslint-plugin": "^4.31.0",
"@typescript-eslint/parser": "^4.31.0",
Expand All @@ -62,7 +63,7 @@
"jest-playwright-preset": "^1.7.0",
"lint-staged": "^11.1.2",
"next": "11.1.2",
"node-fetch": "^2.6.1",
"node-fetch": "^2.6.2",
"np": "^7.5.0",
"playwright": "^1.14.1",
"prettier": "^2.4.0",
Expand Down
33 changes: 33 additions & 0 deletions pages/login.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { handle, json, redirect } from '../src';
import { Form, usePendingFormSubmit } from '../src/form';
import { streamToBuffer } from '../src/utils';

type PageProps = any;
const sleep = (duration) =>
new Promise((resolve) => setTimeout(resolve, duration));

export const getServerSideProps = handle<PageProps>({
async upload({ file, stream }) {
file.contents = (await streamToBuffer(stream)).toString('utf-8');
},

async get({ query }) {
return json(query);
},

async post({ req: { body } }) {
return redirect('/form-component');
},
});

export default function LoginPage(props: PageProps) {
const Tag = typeof props.nojs === 'undefined' ? Form : 'form';

return (
<Tag method="post">
<input name="username" type="text" />
<input name="password" type="password" />
<input type="submit" />
</Tag>
);
}
130 changes: 25 additions & 105 deletions src/handle.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,33 @@ import FormData from 'form-data';

import { next } from './__utils__/server';
import { handle } from './handle';
import { json, notFound, redirect } from './responses';
import { json } from './responses';
import { streamToBuffer } from './utils';

test('handles next native return shapes', async () => {
const fetch = await next(
handle<{ name: string }>({
async get() {
return { props: { name: 'person' } };
},
}),
);

const standardResponse = await fetch('/', {
method: 'GET',
headers: { accept: 'text/html' },
});
expect(standardResponse.status).toEqual(200);
expect(standardResponse.body).toEqual({ props: { name: 'person' } });

const jsonResponse = await fetch();
expect(jsonResponse.status).toEqual(200);
expect(jsonResponse.body).toEqual({ name: 'person' });
});

test('handles to get requests', async () => {
const fetch = await next(
handle({
handle<{ method: string }>({
async get({ req: { method } }) {
return json({ method });
},
Expand Down Expand Up @@ -111,9 +132,10 @@ test('returns notFound when method handler is undefined', async () => {

const response = await fetch('/', {
method: 'POST',
body: '',
});

expect(response.body).toEqual({ notFound: true });
expect(response.status).toEqual(404);
});

test('expands post body', async () => {
Expand Down Expand Up @@ -300,105 +322,3 @@ test('handles file size limit', async () => {
],
});
});

test('handles redirects', async () => {
const fetch = await next(
handle({
async get() {
return redirect('/other-page', { permanent: true });
},
}),
);

const response = await fetch('/', {
method: 'GET',
headers: { accept: 'text/html' },
});

expect(response.body).toEqual({
redirect: { destination: '/other-page', permanent: true },
});
});

test('handles notFound', async () => {
const fetch = await next(
handle({
async get() {
return notFound();
},
}),
);

const response = await fetch('/', {
method: 'GET',
headers: { accept: 'text/html' },
});

expect(response.body).toEqual({ notFound: true });
});

test('can read request headers', async () => {
const fetch = await next(
handle({
async get({ req }) {
return json({ header: req.getHeader('x-custom') });
},
}),
);

const response = await fetch('/', {
headers: { 'x-custom': 'one' },
});

expect(response.body).toEqual({ header: 'one' });
});

test('can set response headers', async () => {
const fetch = await next(
handle({
async get({ req: { method }, res }) {
res.setHeader('session', 'one');
// note: this requires node > 16.4
return json({ method }, { headers: { 'alt-syntax': 'two' } });
},
}),
);

const response = await fetch('/');
expect(response.headers.get('session')).toEqual('one');
expect(response.headers.get('alt-syntax')).toEqual('two');
});

test('can get request cookies', async () => {
const fetch = await next(
handle({
async get({ req }) {
return json({ cookie: req.getCookie('session') });
},
}),
);

const response = await fetch('/', {
cookies: {
session: 'one',
},
});

expect(response.body).toEqual({ cookie: 'one' });
});

test('can set response cookies', async () => {
const fetch = await next(
handle({
async get({ req: { method }, res }) {
res.setCookie('session', 'two');
return json({ method });
},
}),
);

const response = await fetch('/');
expect(response.headers.get('set-cookie')).toEqual(
'session=two; path=/; httponly',
);
});
Loading

0 comments on commit f1a5e32

Please sign in to comment.