Files
muninn/sidecars/reddit-post/tests/reddit-post.spec.js
T
Doar MihaiandClaude Sonnet 5 f794a5ae83 docs: annotate verified vs unverified selectors, drop redundant clicks
Addresses reviewer's minor findings on the prior fix commit: makes the
screenshot-verified/unverified boundary explicit in code comments so it
travels with the file rather than living only in commit messages, and
removes redundant .click() calls before .fill() (fill() already focuses).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-18 18:40:54 +03:00

131 lines
5.2 KiB
JavaScript

// @ts-check
const { test, expect } = require('@playwright/test');
const fs = require('fs');
const path = require('path');
const RESULT_PATH = path.join(__dirname, '..', 'test-results', 'result.json');
function writeResult(result) {
fs.mkdirSync(path.dirname(RESULT_PATH), { recursive: true });
fs.writeFileSync(RESULT_PATH, JSON.stringify(result));
}
test.describe('reddit post', () => {
let outcome = { success: false, error: 'Test did not complete' };
let outcomeFinalized = false;
test.afterEach(async () => {
writeResult(outcome);
});
test('logs into Reddit and creates a post', async ({ page }) => {
const username = process.env.REDDIT_USERNAME;
const password = process.env.REDDIT_PASSWORD;
const subreddit = process.env.SUBREDDIT;
const title = process.env.POST_TITLE;
const body = process.env.POST_BODY;
for (const [name, value] of Object.entries({
REDDIT_USERNAME: username,
REDDIT_PASSWORD: password,
SUBREDDIT: subreddit,
POST_TITLE: title,
POST_BODY: body,
})) {
if (!value) {
outcome = { success: false, error: `Missing required environment variable: ${name}` };
outcomeFinalized = true;
throw new Error(outcome.error);
}
}
try {
await page.goto('https://www.reddit.com/login/');
const blockedHeading = page.getByText(/blocked by network security/i);
if (await blockedHeading.isVisible({ timeout: 3_000 }).catch(() => false)) {
outcome = {
success: false,
error: 'Reddit blocked this request at the network level ("blocked by network security") before login could be attempted — this is not a credentials or selector issue, it means the current network/IP is being flagged as automation by Reddit',
};
outcomeFinalized = true;
throw new Error(outcome.error);
}
// Screenshot-verified against the live Reddit login page: cookie
// dialog button text and the "Log In" submit button's accessible
// name are both confirmed correct.
const rejectCookies = page.getByRole('button', { name: 'Reject Optional Cookies' });
if (await rejectCookies.isVisible({ timeout: 5_000 }).catch(() => false)) {
await rejectCookies.click();
}
// Not individually screenshot-verified (only confirmed to resolve
// as far as the network block allowed) — watch these first if
// login fails on a real, unblocked network.
const usernameField = page.getByLabel(/email or username/i);
await usernameField.fill(/** @type {string} */ (username));
const passwordField = page.getByLabel(/^password/i);
await passwordField.fill(/** @type {string} */ (password));
await page.getByRole('button', { name: 'Log In' }).click();
try {
await expect(page.locator('span[name="avatar"], #USER_DROPDOWN_ID, [icon-name="account-circle-outline"]').first()).toBeVisible({ timeout: 20_000 });
} catch {
const loginErrorBanner = page.getByText(/something went wrong logging in|invalid username|invalid password/i);
const bannerText = await loginErrorBanner.first().textContent().catch(() => null);
outcome = {
success: false,
error: bannerText
? `Login failed: ${bannerText.trim()}`
: 'Login did not complete — possible CAPTCHA, 2FA prompt, or invalid credentials',
};
outcomeFinalized = true;
throw new Error(outcome.error);
}
// UNVERIFIED: a network block in the dev sandbox prevented ever
// reaching this point in a live run, so none of the selectors below
// (text-type tab, title field, body field, submit button) have been
// confirmed against the real submit page. If posting fails here on
// a real, unblocked network, inspect the live page first — e.g. via
// `npx playwright codegen https://www.reddit.com/r/test/submit`.
await page.goto(`https://www.reddit.com/r/${subreddit}/submit`);
const textTab = page.getByRole('button', { name: /^Text$/i }).first();
if (await textTab.isVisible({ timeout: 10_000 }).catch(() => false)) {
await textTab.click();
}
await page.getByPlaceholder(/title/i).first().fill(/** @type {string} */ (title));
const bodyField = page.locator('[data-testid="post-composer-rte"], div[role="textbox"]').first();
await bodyField.click();
await bodyField.fill(/** @type {string} */ (body));
const submitButton = page.getByRole('button', { name: /^Post$/i }).first();
await expect(submitButton).toBeEnabled({ timeout: 10_000 });
await submitButton.click();
try {
await page.waitForURL(/\/comments\//, { timeout: 20_000 });
} catch {
outcome = { success: false, error: 'Post submission did not complete — no redirect to the new post' };
outcomeFinalized = true;
throw new Error(outcome.error);
}
outcome = { success: true, url: page.url() };
outcomeFinalized = true;
expect(outcome.success).toBe(true);
} catch (err) {
if (!outcomeFinalized) {
outcome = { success: false, error: err instanceof Error ? err.message : String(err) };
}
throw err;
}
});
});