Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 22 additions & 6 deletions 28 site/e2e/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { type ChildProcess, exec, spawn } from "node:child_process";
import { randomUUID } from "node:crypto";
import type { Server } from "node:http";
import net from "node:net";
import path from "node:path";
import { Duplex } from "node:stream";
Expand Down Expand Up @@ -865,17 +866,32 @@ export class Awaiter {
}
}

export const createServer = async (
port: number,
): Promise<ReturnType<typeof express>> => {
type MockServer = {
app: ReturnType<typeof express>;
/** Stops the server and drops keep-alive connections. */
close: () => Promise<void>;
};

export const createServer = async (port: number): Promise<MockServer> => {
await waitForPort(port); // Wait until the port is available

const e = express();
const app = express();
// We need to specify the local IP address as the web server
// tends to fail with IPv6 related error:
// listen EADDRINUSE: address already in use :::50516
await new Promise<void>((r) => e.listen(port, "0.0.0.0", r));
return e;
const server = await new Promise<Server>((resolve) => {
const s = app.listen(port, "0.0.0.0", () => resolve(s));
});

return {
app,
close: () =>
new Promise<void>((resolve, reject) => {
// Order matters: stop accepting, then drop keep-alives.
server.close((err) => (err ? reject(err) : resolve()));
server.closeAllConnections?.();
}),
};
};

async function waitForPort(
Expand Down
85 changes: 49 additions & 36 deletions 85 site/e2e/tests/externalAuth.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@ import { beforeCoderTest, resetExternalAuthKey } from "../hooks";

test.describe
.skip("externalAuth", () => {
let closeWebServer: (() => Promise<void>) | undefined;

test.beforeAll(async ({ baseURL }) => {
const srv = await createServer(gitAuth.webPort);
const { app: srv, close } = await createServer(gitAuth.webPort);
closeWebServer = close;

// The GitHub validate endpoint returns the currently authenticated user!
srv.use(gitAuth.validatePath, (_req, res) => {
Expand All @@ -34,6 +37,10 @@ test.describe
});
});

test.afterAll(async () => {
await closeWebServer?.();
});

test.beforeEach(async ({ context, page }) => {
beforeCoderTest(page);
await login(page);
Expand All @@ -51,43 +58,49 @@ test.describe
};

// Start a server to mock the GitHub API.
const srv = await createServer(gitAuth.devicePort);
srv.use(gitAuth.validatePath, (_req, res) => {
res.write(JSON.stringify(ghUser));
res.end();
});
srv.use(gitAuth.codePath, (_req, res) => {
res.write(JSON.stringify(device));
res.end();
});
srv.use(gitAuth.installationsPath, (_req, res) => {
res.write(JSON.stringify(ghInstall));
res.end();
});
const { app: srv, close: closeServer } = await createServer(
gitAuth.devicePort,
);
try {
srv.use(gitAuth.validatePath, (_req, res) => {
res.write(JSON.stringify(ghUser));
res.end();
});
srv.use(gitAuth.codePath, (_req, res) => {
res.write(JSON.stringify(device));
res.end();
});
srv.use(gitAuth.installationsPath, (_req, res) => {
res.write(JSON.stringify(ghInstall));
res.end();
});

const token = {
access_token: "",
error: "authorization_pending",
error_description: "",
};
// First we send a result from the API that the token hasn't been
// authorized yet to ensure the UI reacts properly.
const sentPending = new Awaiter();
srv.use(gitAuth.tokenPath, (_req, res) => {
res.write(JSON.stringify(token));
res.end();
sentPending.done();
});
const token = {
access_token: "",
error: "authorization_pending",
error_description: "",
};
// First we send a result from the API that the token hasn't been
// authorized yet to ensure the UI reacts properly.
const sentPending = new Awaiter();
srv.use(gitAuth.tokenPath, (_req, res) => {
res.write(JSON.stringify(token));
res.end();
sentPending.done();
});

await page.goto(`/external-auth/${gitAuth.deviceProvider}`, {
waitUntil: "domcontentloaded",
});
await page.getByText(device.user_code).isVisible();
await sentPending.wait();
// Update the token to be valid and ensure the UI updates!
token.error = "";
token.access_token = "hello-world";
await page.waitForSelector("text=1 organization authorized");
await page.goto(`/external-auth/${gitAuth.deviceProvider}`, {
waitUntil: "domcontentloaded",
});
await page.getByText(device.user_code).isVisible();
await sentPending.wait();
// Update the token to be valid and ensure the UI updates!
token.error = "";
token.access_token = "hello-world";
await page.waitForSelector("text=1 organization authorized");
} finally {
await closeServer();
}
});

test("external auth web", async ({ page }) => {
Expand Down
Loading
Morty Proxy This is a proxified and sanitized view of the page, visit original site.