Show Reddit recovery status in dashboard
This commit is contained in:
1 parent
d2b3a1b31d
commit
316dc3dd3e
6 files changed
+208
-5
No files matched your search
@@ -0,0 +1,16 @@
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
describe('dashboard Reddit session banner', () => {
|
||||
it('renders a persistent recovery banner with a Reddit login action', () => {
|
||||
const html = fs.readFileSync(path.resolve('public/index.html'), 'utf8');
|
||||
|
||||
expect(html).toContain('id="reddit-session-banner"');
|
||||
expect(html).toContain('id="reddit-session-banner-title"');
|
||||
expect(html).toContain('Open Reddit Login');
|
||||
expect(html).toContain("data.status === 'unreachable'");
|
||||
expect(html).toContain("data.status === 'login-required'");
|
||||
expect(html).toContain('info.publicUrl');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,81 @@
|
||||
import express from 'express';
|
||||
import type { AddressInfo } from 'node:net';
|
||||
import { afterEach, describe, expect, it } from 'vitest';
|
||||
import { classifyRedditSessionError } from '../src/browser/client.js';
|
||||
import { createLoginRouter } from '../src/web/routes/login.js';
|
||||
|
||||
const servers: Array<ReturnType<express.Express['listen']>> = [];
|
||||
|
||||
async function request(app: express.Express, path: string): Promise<Response> {
|
||||
const server = app.listen(0);
|
||||
servers.push(server);
|
||||
await new Promise<void>((resolve) => server.once('listening', resolve));
|
||||
const { port } = server.address() as AddressInfo;
|
||||
return fetch(`http://127.0.0.1:${port}${path}`);
|
||||
}
|
||||
|
||||
afterEach(async () => {
|
||||
await Promise.all(
|
||||
servers.splice(0).map(
|
||||
(server) => new Promise<void>((resolve) => server.close(() => resolve())),
|
||||
),
|
||||
);
|
||||
delete process.env.NOVNC_PUBLIC_URL;
|
||||
});
|
||||
|
||||
function appWithBrowser(state: 'authenticated' | 'login-required' | 'unreachable') {
|
||||
const app = express();
|
||||
app.use(
|
||||
createLoginRouter({
|
||||
services: {
|
||||
browser: {
|
||||
isSessionValid: async () => state === 'authenticated',
|
||||
getSessionState: () => state,
|
||||
},
|
||||
},
|
||||
logger: { error: () => undefined },
|
||||
} as never),
|
||||
);
|
||||
return app;
|
||||
}
|
||||
|
||||
describe('Reddit session status', () => {
|
||||
it('reports when Reddit is unreachable instead of presenting it as logged out', async () => {
|
||||
const response = await request(appWithBrowser('unreachable'), '/login/status');
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(await response.json()).toEqual({
|
||||
ready: true,
|
||||
loggedIn: false,
|
||||
status: 'unreachable',
|
||||
});
|
||||
});
|
||||
|
||||
it('reports when an interactive Reddit login is required', async () => {
|
||||
const response = await request(appWithBrowser('login-required'), '/login/status');
|
||||
|
||||
expect(await response.json()).toMatchObject({
|
||||
loggedIn: false,
|
||||
status: 'login-required',
|
||||
});
|
||||
});
|
||||
|
||||
it('returns the configured public noVNC URL', async () => {
|
||||
process.env.NOVNC_PUBLIC_URL = 'https://red-vnc.apps.jbrunton.co.uk';
|
||||
|
||||
const response = await request(appWithBrowser('authenticated'), '/login/info');
|
||||
|
||||
expect(await response.json()).toMatchObject({
|
||||
publicUrl: 'https://red-vnc.apps.jbrunton.co.uk',
|
||||
});
|
||||
});
|
||||
|
||||
it('classifies Reddit navigation failures as unreachable', () => {
|
||||
expect(
|
||||
classifyRedditSessionError(
|
||||
new Error('page.goto: net::ERR_HTTP_RESPONSE_CODE_FAILURE at https://www.reddit.com/'),
|
||||
),
|
||||
).toBe('unreachable');
|
||||
expect(classifyRedditSessionError(new Error('login form was not found'))).toBe('login-required');
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user