Files
perinco-CRM/server/test/linkedinPhoto.test.mjs
T
Sascha WolfandClaude Opus 5 0a2cf52e8a feat(kontakte): Zustaendigkeit, LinkedIn-Foto, Netzwerkauswahl
Drei Verbesserungen an den Kontakten:

* Beim Anlegen ist der angemeldete Benutzer voreingestellt zustaendig.
  Bisher stand dort der erste Benutzer der Liste - fast immer falsch.
* Wird eine LinkedIn-Adresse hinterlegt, holt das CRM das oeffentliche
  Profilfoto und setzt es als Kontaktfoto: beim Verlassen des Feldes
  einmal je Adresse, solange kein Foto gesetzt ist, und auf Klick ueber
  den Knopf daneben. Der Abruf laeuft ueber das Backend
  (server/src/linkedinPhoto.js, POST /api/leads/linkedin-photo), weil
  ihn die Same-Origin-Policy im Browser verhindert. Gelesen wird die
  Open-Graph-Vorschau der Profilseite; Herkunft, Groesse und Antwortzeit
  des Bildes werden geprueft, Platzhalter-Silhouetten abgewiesen.
  Schlaegt der Abruf fehl, steht der Grund unter dem Feld.
* In der Netzwerkansicht hebt ein Klick auf freie Flaeche die Auswahl
  auf - das Diagramm sieht danach aus wie beim Wechsel in die Ansicht.
  Ein Ziehen des Hintergrunds (ab 4 px) laesst die Auswahl stehen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-09 23:25:37 +02:00

83 lines
3.0 KiB
JavaScript

/*
* Pruefungen des LinkedIn-Profilfotos: laeuft mit `npm test` im Verzeichnis
* server/. Geprueft wird alles, was ohne Netz geht - die Adresspruefung und das
* Herausloesen der Bildadresse aus dem Seitenquelltext. Der Abruf selbst haengt
* an LinkedIn und taugt nicht als Test: er kann jederzeit abgewiesen werden.
*/
const { normalizeProfileUrl, extractImageUrl } = await import('../src/linkedinPhoto.js');
let failures = 0;
function check(name, condition, detail = '') {
if (condition) {
console.log(` ok ${name}${detail ? ` - ${detail}` : ''}`);
} else {
console.log(` FAIL ${name}${detail ? ` - ${detail}` : ''}`);
failures += 1;
}
}
function throws(fn) {
try {
fn();
return null;
} catch (err) {
return err;
}
}
console.log('\nAdresse pruefen');
check(
'Vollstaendige Adresse bleibt',
normalizeProfileUrl('https://www.linkedin.com/in/max') === 'https://www.linkedin.com/in/max'
);
check(
'Ohne Schema wird https ergaenzt',
normalizeProfileUrl('linkedin.com/in/max') === 'https://linkedin.com/in/max'
);
check(
'Landeskennung ist erlaubt',
normalizeProfileUrl('https://de.linkedin.com/in/max') === 'https://de.linkedin.com/in/max'
);
check(
'Nachverfolgung faellt weg',
normalizeProfileUrl('https://www.linkedin.com/in/max?trk=abc#top') ===
'https://www.linkedin.com/in/max'
);
check(
'http wird zu https',
normalizeProfileUrl('http://www.linkedin.com/in/max') === 'https://www.linkedin.com/in/max'
);
const fremd = throws(() => normalizeProfileUrl('https://linkedin.com.example.org/in/max'));
check('Aehnlich aussehender Host wird abgewiesen', fremd?.status === 400, fremd?.message);
check('Fremder Host wird abgewiesen', throws(() => normalizeProfileUrl('https://xing.com/x'))?.status === 400);
check('Leere Eingabe wird abgewiesen', throws(() => normalizeProfileUrl(''))?.status === 400);
check('Unsinn wird abgewiesen', throws(() => normalizeProfileUrl('kein ://link'))?.status === 400);
console.log('\nBildadresse aus dem Quelltext');
check(
'og:image wird gefunden',
extractImageUrl('<meta property="og:image" content="https://media.licdn.com/dms/image/v2/abc" />') ===
'https://media.licdn.com/dms/image/v2/abc'
);
check(
'Maskierte Und-Zeichen werden zurueckgesetzt',
extractImageUrl('<meta property="og:image" content="https://media.licdn.com/dms/image/a?x=1&amp;y=2">') ===
'https://media.licdn.com/dms/image/a?x=1&y=2'
);
check(
'twitter:image als Ersatz',
extractImageUrl('<meta name="twitter:image" content="https://media.licdn.com/dms/image/t">') ===
'https://media.licdn.com/dms/image/t'
);
check(
'Ohne Vorschau greift die Suche im Quelltext',
extractImageUrl('var x = "https://media.licdn.com/dms/image/v2/zzz/profile-displayphoto";') ===
'https://media.licdn.com/dms/image/v2/zzz/profile-displayphoto'
);
check('Seite ohne Bild liefert null', extractImageUrl('<html><body>nichts</body></html>') === null);
console.log(failures === 0 ? '\nAlle Pruefungen bestanden.' : `\n${failures} Pruefung(en) fehlgeschlagen.`);
process.exit(failures === 0 ? 0 : 1);