Anlass: Bei einem Kontakt kam das Foto, beim naechsten nicht. Der Vergleich der beiden Profilseiten zeigt warum - das zweite Profil liefert als og:image nur LinkedIns Ersatzgrafik (static.licdn.com/aero-v1/sc/h/...), weil die Sichtbarkeit des Fotos auf "Meine Kontakte" steht. Angemeldet sieht man es, der Abruf hier ist es nicht. Zwei Konsequenzen: * Die Rueckfallebene "erste media.licdn.com-Adresse im Quelltext" faellt weg. Dieselbe Seite enthaelt 24 solcher Adressen: Firmenlogos, Beitragsbilder und die Fotos der "aehnlichen Profile" am Rand. Nur die Open-Graph-Vorschau gehoert nachweislich zum aufgerufenen Profil - alles andere haette irgendwann ein fremdes Gesicht an einen Kontakt gehaengt. * Die Meldung sagt jetzt, was wirklich los ist: "nur fuer Kontakte sichtbar oder es gibt keines" statt "hat kein eigenes Foto". Von aussen sind die beiden Faelle nicht unterscheidbar. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
95 lines
3.5 KiB
JavaScript
95 lines
3.5 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('Seite ohne Vorschau liefert null', extractImageUrl('<html><body>nichts</body></html>') === null);
|
|
/*
|
|
* Wichtig: ohne og:image wird NICHT im uebrigen Quelltext gesucht. Eine
|
|
* Profilseite ist voller fremder Bilder - Firmenlogos, Beitragsbilder, Fotos
|
|
* der "aehnlichen Profile". Eines davon zu nehmen, haenge dem Kontakt das
|
|
* Gesicht einer fremden Person an.
|
|
*/
|
|
check(
|
|
'Fremde Bilder im Quelltext werden nicht genommen',
|
|
extractImageUrl(
|
|
'<html><body><img src="https://media.licdn.com/dms/image/v2/xy/profile-displayphoto-shrink_400_400/0/1"></body></html>'
|
|
) === null
|
|
);
|
|
check(
|
|
'Ersatzgrafik von static.licdn.com kommt als solche zurueck',
|
|
extractImageUrl('<meta property="og:image" content="https://static.licdn.com/aero-v1/sc/h/abc">') ===
|
|
'https://static.licdn.com/aero-v1/sc/h/abc'
|
|
);
|
|
|
|
console.log(failures === 0 ? '\nAlle Pruefungen bestanden.' : `\n${failures} Pruefung(en) fehlgeschlagen.`);
|
|
process.exit(failures === 0 ? 0 : 1);
|