Ctrl K
ComunidadGuías…

__tests__/postwatch-extract.test.js

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
/**
 * The extractor against REAL Facebook pages, saved as gzipped fixtures (captured with the watcher's
 * probe, `html:true`). Every DOM fact learned on the live post is pinned here: comment/reply articles
 * by aria-label, author + reply target from the label with the time suffix stripped, the Follow badge
 * kept out of the text, the hidden duplicate copy ignored, the post body found and not a sidebar
 * suggestion. Runs the page in a real Chromium (layout matters: visibility is part of the rules).
 * Skips cleanly where the fixtures or a launchable Chromium are not present (the VPS host has no
 * browser deps; the pod image and CI do).
 */
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import fs from 'node:fs';
import path from 'node:path';
import zlib from 'node:zlib';
import { extractInPage } from '../src/postWatch.js';

const FIX = path.join(__dirname, 'fixtures', 'fb');
const have = (f) => fs.existsSync(path.join(FIX, f + '.gz'));
const read = (f) => zlib.gunzipSync(fs.readFileSync(path.join(FIX, f + '.gz'))).toString('utf8');

let chromium = null, browser = null;
try { chromium = (await import('playwright')).chromium; } catch { chromium = null; }
if (chromium && have('post-page.html')) {
  try { browser = await chromium.launch({ args: ['--no-sandbox', '--disable-dev-shm-usage', '--disable-gpu'] }); }
  catch (e) { browser = null; console.warn(`[postwatch-extract] no launchable Chromium here (${String(e.message).split('\n')[0]}) — skipping; runs in the pod image / CI`); }
}

describe.skipIf(!browser)('post watcher extractor on real pages', () => {
  let page;
  beforeAll(async () => { page = await browser.newPage({ viewport: { width: 1280, height: 2400 } }); await page.route('**/*', (r) => r.abort()).catch(() => {}); }, 60000);
  afterAll(async () => { try { await browser.close(); } catch { /* gone */ } });

  const load = async (file, url) => {
    await page.goto(url, { waitUntil: 'commit' }).catch(() => {});   // location.href carries the ids
    await page.setContent(read(file), { waitUntil: 'domcontentloaded' });
    return page.evaluate(extractInPage);
  };

  it('post page: root comments with clean authors, no time suffix, no Follow badge in the text, the post itself', async () => {
    const t = await load('post-page.html', 'https://www.facebook.com/groups/vibecodinglife/posts/2137606046827921/');
    expect(t.postId).toBe('2137606046827921');
    expect(t.nodes.length).toBeGreaterThan(5);
    for (const n of t.nodes) {
      expect(n.author).not.toMatch(/geleden|ago|\d+\s*u$/i);
      expect(n.text).not.toMatch(/^volgen$|^follow$/i);
      expect(n.author).not.toMatch(/ op (het antwoord|de opmerking) van /i);
    }
    expect(t.postText).toMatch(/70% wall/i);                                  // the post, not a sidebar suggestion
    expect(new Set(t.nodes.map((n) => n.id)).size).toBe(t.nodes.length);    // one node per id
  }, 60000);

  it.skipIf(!have('reply-page.html'))('reply page: author AND reply target from the label, replies-to-replies present, owner recognised', async () => {
    const t = await load('reply-page.html', 'https://www.facebook.com/groups/vibecodinglife/posts/2137606046827921/?comment_id=2137644956824030&reply_comment_id=2138317006756825');
    const replies = t.nodes.filter((n) => n.isReply);
    expect(replies.length).toBeGreaterThan(0);
    expect(replies.some((n) => n.replyTo && n.replyTo.length > 2)).toBe(true);
    expect(t.nodes.some((n) => /wesley stoep/i.test(n.author))).toBe(true);
    for (const n of replies) { expect(n.cid).toBeTruthy(); expect(n.rid || n.id).toBeTruthy(); }
  }, 60000);
});