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>
83 lines
3.0 KiB
JavaScript
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&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);
|