forked from penx/remix-google-cloud-functions
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
10 changed files
with
462 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
/node_modules |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# Welcome to Remix! | ||
|
||
[Remix](https://remix.run) is a web framework that helps you build better websites with React. | ||
|
||
To get started, open a new shell and run: | ||
|
||
```sh | ||
npx create-remix@latest | ||
``` | ||
|
||
Then follow the prompts you see in your terminal. | ||
|
||
For more information about Remix, [visit remix.run](https://remix.run)! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,272 @@ | ||
import supertest from "supertest"; | ||
import { createRequest } from "node-mocks-http"; | ||
import { | ||
createRequestHandler as createRemixRequestHandler, | ||
Response as NodeResponse, | ||
} from "@remix-run/node"; | ||
import { Readable } from "stream"; | ||
import { http } from "@google-cloud/functions-framework"; | ||
// @ts-ignore | ||
import { getTestServer } from "@google-cloud/functions-framework/testing"; | ||
|
||
import { | ||
createRemixHeaders, | ||
createRemixRequest, | ||
createRequestHandler, | ||
} from "../server"; | ||
|
||
// We don't want to test that the remix server works here (that's what the | ||
// puppetteer tests do), we just want to test the express adapter | ||
jest.mock("@remix-run/node", () => { | ||
let original = jest.requireActual("@remix-run/node"); | ||
return { | ||
...original, | ||
createRequestHandler: jest.fn(), | ||
}; | ||
}); | ||
let mockedCreateRequestHandler = | ||
createRemixRequestHandler as jest.MockedFunction< | ||
typeof createRemixRequestHandler | ||
>; | ||
|
||
function createApp() { | ||
http( | ||
"remixServer", | ||
createRequestHandler({ | ||
// We don't have a real app to test, but it doesn't matter. We | ||
// won't ever call through to the real createRequestHandler | ||
build: undefined, | ||
}) | ||
); | ||
return getTestServer("remixServer"); | ||
} | ||
|
||
describe("express createRequestHandler", () => { | ||
describe("basic requests", () => { | ||
afterEach(() => { | ||
mockedCreateRequestHandler.mockReset(); | ||
}); | ||
|
||
afterAll(() => { | ||
jest.restoreAllMocks(); | ||
}); | ||
|
||
it("handles requests", async () => { | ||
mockedCreateRequestHandler.mockImplementation(() => async (req) => { | ||
return new Response(`URL: ${new URL(req.url).pathname}`); | ||
}); | ||
|
||
let request = supertest(createApp()); | ||
let res = await request.get("/foo/bar"); | ||
|
||
expect(res.status).toBe(200); | ||
expect(res.text).toBe("URL: /foo/bar"); | ||
expect(res.headers["x-powered-by"]).toBe(undefined); | ||
}); | ||
|
||
it("handles null body", async () => { | ||
mockedCreateRequestHandler.mockImplementation(() => async () => { | ||
return new Response(null, { status: 200 }); | ||
}); | ||
|
||
let request = supertest(createApp()); | ||
let res = await request.get("/"); | ||
|
||
expect(res.status).toBe(200); | ||
}); | ||
|
||
// https://github.com/node-fetch/node-fetch/blob/4ae35388b078bddda238277142bf091898ce6fda/test/response.js#L142-L148 | ||
it("handles body as stream", async () => { | ||
mockedCreateRequestHandler.mockImplementation(() => async () => { | ||
let stream = Readable.from("hello world"); | ||
return new NodeResponse(stream, { status: 200 }) as unknown as Response; | ||
}); | ||
|
||
let request = supertest(createApp()); | ||
// note: vercel's createServerWithHelpers requires a x-now-bridge-request-id | ||
let res = await request.get("/").set({ "x-now-bridge-request-id": "2" }); | ||
|
||
expect(res.status).toBe(200); | ||
expect(res.text).toBe("hello world"); | ||
}); | ||
|
||
it("handles status codes", async () => { | ||
mockedCreateRequestHandler.mockImplementation(() => async () => { | ||
return new Response(null, { status: 204 }); | ||
}); | ||
|
||
let request = supertest(createApp()); | ||
let res = await request.get("/"); | ||
|
||
expect(res.status).toBe(204); | ||
}); | ||
|
||
it("sets headers", async () => { | ||
mockedCreateRequestHandler.mockImplementation(() => async () => { | ||
let headers = new Headers({ "X-Time-Of-Year": "most wonderful" }); | ||
headers.append( | ||
"Set-Cookie", | ||
"first=one; Expires=0; Path=/; HttpOnly; Secure; SameSite=Lax" | ||
); | ||
headers.append( | ||
"Set-Cookie", | ||
"second=two; MaxAge=1209600; Path=/; HttpOnly; Secure; SameSite=Lax" | ||
); | ||
headers.append( | ||
"Set-Cookie", | ||
"third=three; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Path=/; HttpOnly; Secure; SameSite=Lax" | ||
); | ||
return new Response(null, { headers }); | ||
}); | ||
|
||
let request = supertest(createApp()); | ||
let res = await request.get("/"); | ||
|
||
expect(res.headers["x-time-of-year"]).toBe("most wonderful"); | ||
expect(res.headers["set-cookie"]).toEqual([ | ||
"first=one; Expires=0; Path=/; HttpOnly; Secure; SameSite=Lax", | ||
"second=two; MaxAge=1209600; Path=/; HttpOnly; Secure; SameSite=Lax", | ||
"third=three; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Path=/; HttpOnly; Secure; SameSite=Lax", | ||
]); | ||
}); | ||
}); | ||
}); | ||
|
||
describe("express createRemixHeaders", () => { | ||
describe("creates fetch headers from express headers", () => { | ||
it("handles empty headers", () => { | ||
expect(createRemixHeaders({})).toMatchInlineSnapshot(` | ||
Headers { | ||
Symbol(query): Array [], | ||
Symbol(context): null, | ||
} | ||
`); | ||
}); | ||
|
||
it("handles simple headers", () => { | ||
expect(createRemixHeaders({ "x-foo": "bar" })).toMatchInlineSnapshot(` | ||
Headers { | ||
Symbol(query): Array [ | ||
"x-foo", | ||
"bar", | ||
], | ||
Symbol(context): null, | ||
} | ||
`); | ||
}); | ||
|
||
it("handles multiple headers", () => { | ||
expect(createRemixHeaders({ "x-foo": "bar", "x-bar": "baz" })) | ||
.toMatchInlineSnapshot(` | ||
Headers { | ||
Symbol(query): Array [ | ||
"x-foo", | ||
"bar", | ||
"x-bar", | ||
"baz", | ||
], | ||
Symbol(context): null, | ||
} | ||
`); | ||
}); | ||
|
||
it("handles headers with multiple values", () => { | ||
expect(createRemixHeaders({ "x-foo": "bar, baz" })) | ||
.toMatchInlineSnapshot(` | ||
Headers { | ||
Symbol(query): Array [ | ||
"x-foo", | ||
"bar, baz", | ||
], | ||
Symbol(context): null, | ||
} | ||
`); | ||
}); | ||
|
||
it("handles headers with multiple values and multiple headers", () => { | ||
expect(createRemixHeaders({ "x-foo": "bar, baz", "x-bar": "baz" })) | ||
.toMatchInlineSnapshot(` | ||
Headers { | ||
Symbol(query): Array [ | ||
"x-foo", | ||
"bar, baz", | ||
"x-bar", | ||
"baz", | ||
], | ||
Symbol(context): null, | ||
} | ||
`); | ||
}); | ||
|
||
it("handles multiple set-cookie headers", () => { | ||
expect( | ||
createRemixHeaders({ | ||
"set-cookie": [ | ||
"__session=some_value; Path=/; Secure; HttpOnly; MaxAge=7200; SameSite=Lax", | ||
"__other=some_other_value; Path=/; Secure; HttpOnly; MaxAge=3600; SameSite=Lax", | ||
], | ||
}) | ||
).toMatchInlineSnapshot(` | ||
Headers { | ||
Symbol(query): Array [ | ||
"set-cookie", | ||
"__session=some_value; Path=/; Secure; HttpOnly; MaxAge=7200; SameSite=Lax", | ||
"set-cookie", | ||
"__other=some_other_value; Path=/; Secure; HttpOnly; MaxAge=3600; SameSite=Lax", | ||
], | ||
Symbol(context): null, | ||
} | ||
`); | ||
}); | ||
}); | ||
}); | ||
|
||
describe("express createRemixRequest", () => { | ||
it("creates a request with the correct headers", async () => { | ||
let expressRequest = createRequest({ | ||
url: "/foo/bar", | ||
method: "GET", | ||
protocol: "http", | ||
hostname: "localhost", | ||
headers: { | ||
"Cache-Control": "max-age=300, s-maxage=3600", | ||
Host: "localhost:3000", | ||
}, | ||
}); | ||
|
||
expect(createRemixRequest(expressRequest)).toMatchInlineSnapshot(` | ||
NodeRequest { | ||
"agent": undefined, | ||
"compress": true, | ||
"counter": 0, | ||
"follow": 20, | ||
"highWaterMark": 16384, | ||
"insecureHTTPParser": false, | ||
"size": 0, | ||
Symbol(Body internals): Object { | ||
"body": null, | ||
"boundary": null, | ||
"disturbed": false, | ||
"error": null, | ||
"size": 0, | ||
"type": null, | ||
}, | ||
Symbol(Request internals): Object { | ||
"headers": Headers { | ||
Symbol(query): Array [ | ||
"cache-control", | ||
"max-age=300, s-maxage=3600", | ||
"host", | ||
"localhost:3000", | ||
], | ||
Symbol(context): null, | ||
}, | ||
"method": "GET", | ||
"parsedURL": "http://localhost:3000/foo/bar", | ||
"redirect": "follow", | ||
"signal": AbortSignal {}, | ||
}, | ||
} | ||
`); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
import { installGlobals } from "@remix-run/node"; | ||
installGlobals(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
import { installGlobals } from "@remix-run/node"; | ||
installGlobals(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
import "./globals"; | ||
|
||
export type { GetLoadContextFunction, RequestHandler } from "./server"; | ||
export { createRequestHandler } from "./server"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
/** @type {import('@jest/types').Config.InitialOptions} */ | ||
module.exports = { | ||
...require("../../jest/jest.config.shared"), | ||
displayName: "google-cloud-functions", | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
{ | ||
"name": "remix-google-cloud-functions", | ||
"description": "Google Cloud functions request handler for Remix", | ||
"version": "0.0.1", | ||
"license": "MIT", | ||
"main": "build/index.js", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/penx/remix", | ||
"directory": "packages/remix-google-cloud-functions" | ||
}, | ||
"bugs": { | ||
"url": "https://github.com/penx/remix/issues" | ||
}, | ||
"scripts": { | ||
"build": "rm -rf build && tsc -b" | ||
}, | ||
"dependencies": { | ||
"@google-cloud/functions-framework": "^3.1.1", | ||
"@remix-run/node": "1.5.1" | ||
}, | ||
"devDependencies": { | ||
"@types/supertest": "^2.0.10", | ||
"node-mocks-http": "^1.10.1", | ||
"supertest": "^6.0.1" | ||
} | ||
} |
Oops, something went wrong.