Files
muninn/sidecars/linkedin-post/tests/linkedin-post.spec.js
T

138 lines
5.0 KiB
JavaScript
Raw Normal View History

// @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('linkedin post', () => {
let outcome = { success: false, error: 'Test did not complete' };
let outcomeFinalized = false;
test.afterEach(async () => {
writeResult(outcome);
});
test('logs into LinkedIn and creates a post', async ({ page }) => {
const email = process.env.LINKEDIN_EMAIL;
const password = process.env.LINKEDIN_PASSWORD;
const postContent = process.env.POST_CONTENT;
for (const [name, value] of Object.entries({
LINKEDIN_EMAIL: email,
LINKEDIN_PASSWORD: password,
POST_CONTENT: postContent,
})) {
if (!value) {
outcome = { success: false, error: `Missing required environment variable: ${name}` };
outcomeFinalized = true;
throw new Error(outcome.error);
}
}
try {
await page.goto('https://www.linkedin.com/login');
// Wait for page to load
await page.waitForLoadState('networkidle');
// Check for login form
const emailField = page.getByLabel(/email|phone/i).first();
if (!await emailField.isVisible({ timeout: 10000 }).catch(() => false)) {
outcome = {
success: false,
error: 'LinkedIn login form not found — page structure may have changed',
};
outcomeFinalized = true;
throw new Error(outcome.error);
}
// Fill email
await emailField.fill(email);
// Fill password
const passwordField = page.getByLabel(/password/i).first();
if (await passwordField.isVisible({ timeout: 10000 }).catch(() => false)) {
await passwordField.fill(password);
}
// Click sign in button
const signInButton = page.getByRole('button', { name: /sign in|signin|log in/i, exact: false }).first();
await signInButton.click();
// Wait for login to complete - check for feed or start post button
try {
await page.waitForURL('https://www.linkedin.com/feed/', { timeout: 20000 });
} catch {
// Check if we're logged in by looking for start post button
const startPostButton = page.getByRole('button', { name: /start a post|post/i, exact: false }).first();
if (!await startPostButton.isVisible({ timeout: 10000 }).catch(() => false)) {
const errorText = await page.textContent('body').catch(() => '');
outcome = {
success: false,
error: errorText.toLowerCase().includes('invalid') || errorText.toLowerCase().includes('incorrect')
? 'Invalid LinkedIn credentials'
: 'LinkedIn login did not complete — possible CAPTCHA, 2FA, or invalid credentials',
};
outcomeFinalized = true;
throw new Error(outcome.error);
}
}
// Click "Start a post" button
const startPostButton = page.getByRole('button', { name: /start a post|post/i, exact: false }).first();
await startPostButton.click();
// Wait for post composer
const postTextarea = page.getByRole('textbox', { name: /what do you want to talk about/i, exact: false }).first();
if (!await postTextarea.isVisible({ timeout: 10000 }).catch(() => false)) {
// Try alternative selector
const altTextarea = page.locator('div[contenteditable="true"]').first();
if (await altTextarea.isVisible({ timeout: 5000 }).catch(() => false)) {
await altTextarea.click();
await page.keyboard.type(postContent);
} else {
outcome = {
success: false,
error: 'LinkedIn post composer not found — UI may have changed',
};
outcomeFinalized = true;
throw new Error(outcome.error);
}
} else {
await postTextarea.fill(postContent);
}
// Click post button
const postButton = page.getByRole('button', { name: /^post$/i, exact: true }).first();
if (await postButton.isVisible({ timeout: 5000 }).catch(() => false)) {
await postButton.click();
} else {
// Try alternative post button selectors
const altPostButton = page.getByRole('button', { name: /share|post/i, exact: false }).nth(1);
if (await altPostButton.isVisible({ timeout: 5000 }).catch(() => false)) {
await altPostButton.click();
}
}
// Wait for post to be created
await page.waitForTimeout(3000);
// Get current URL (should be the post URL or feed)
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;
}
});
});