132 lines
5 KiB
TypeScript
132 lines
5 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
import { parseAddress } from '../src/shell/addressModel.ts';
|
|
|
|
function labels(url: string, wording: 'twitter' | 'x' = 'x') {
|
|
const model = parseAddress(url, wording);
|
|
return model ? model.segments.map((s) => s.label) : null;
|
|
}
|
|
|
|
test('home', () => {
|
|
assert.deepEqual(labels('https://x.com/home'), ['Home']);
|
|
assert.deepEqual(labels('https://x.com/'), ['Home']);
|
|
});
|
|
|
|
test('search with quoted truncated query', () => {
|
|
const long = 'a'.repeat(60);
|
|
const model = parseAddress(`https://x.com/search?q=${long}`, 'x');
|
|
assert.ok(model);
|
|
assert.equal(model!.segments[0].label, 'Search');
|
|
const quoted = model!.segments[1];
|
|
assert.ok(quoted.quoted);
|
|
assert.ok(quoted.label.length <= 42);
|
|
assert.ok(quoted.label.endsWith('…"'));
|
|
});
|
|
|
|
test('notifications and mentions', () => {
|
|
assert.deepEqual(labels('https://x.com/notifications'), ['Notifications']);
|
|
assert.deepEqual(labels('https://x.com/notifications/mentions'), ['Notifications', 'Mentions']);
|
|
});
|
|
|
|
test('messages with mono last6 id', () => {
|
|
const model = parseAddress('https://x.com/messages/1234567890', 'x');
|
|
assert.ok(model);
|
|
assert.equal(model!.segments[0].label, 'Messages');
|
|
assert.equal(model!.segments[1].label, '…567890');
|
|
assert.ok(model!.segments[1].mono);
|
|
});
|
|
|
|
test('bookmarks, lists and communities', () => {
|
|
assert.deepEqual(labels('https://x.com/i/bookmarks'), ['Bookmarks']);
|
|
assert.deepEqual(labels('https://x.com/i/lists/9876543210'), ['Lists', '…543210']);
|
|
assert.deepEqual(labels('https://x.com/i/communities/1112223334'), ['Communities', '…223334']);
|
|
});
|
|
|
|
test('explore with capitalised tab', () => {
|
|
assert.deepEqual(labels('https://x.com/explore'), ['Explore']);
|
|
assert.deepEqual(labels('https://x.com/explore/tabs/trending'), ['Explore', 'Trending']);
|
|
});
|
|
|
|
test('settings parts are title-cased', () => {
|
|
assert.deepEqual(labels('https://x.com/settings/account/username'), ['Settings', 'Account', 'Username']);
|
|
});
|
|
|
|
test('user profile chip', () => {
|
|
assert.deepEqual(labels('https://x.com/jack'), ['@jack']);
|
|
});
|
|
|
|
test('reserved paths are not treated as usernames', () => {
|
|
assert.deepEqual(labels('https://x.com/home'), ['Home']);
|
|
assert.deepEqual(labels('https://x.com/explore'), ['Explore']);
|
|
assert.deepEqual(labels('https://x.com/settings'), ['Settings']);
|
|
assert.deepEqual(labels('https://x.com/i/bookmarks'), ['Bookmarks']);
|
|
});
|
|
|
|
test('status url with mono last6 id', () => {
|
|
const model = parseAddress('https://x.com/jack/status/1234567890123', 'x');
|
|
assert.ok(model);
|
|
assert.deepEqual(
|
|
model!.segments.map((s) => s.label),
|
|
['@jack', 'Post', '…890123'],
|
|
);
|
|
assert.ok(model!.segments[2].mono);
|
|
});
|
|
|
|
test('status photo suffix', () => {
|
|
assert.deepEqual(
|
|
labels('https://x.com/jack/status/1234567890123/photo/2'),
|
|
['@jack', 'Post', '…890123', 'Photo 2'],
|
|
);
|
|
});
|
|
|
|
test('status analytics suffix', () => {
|
|
assert.deepEqual(
|
|
labels('https://x.com/jack/status/1234567890123/analytics'),
|
|
['@jack', 'Post', '…890123', 'Analytics'],
|
|
);
|
|
});
|
|
|
|
test('user subpages', () => {
|
|
assert.deepEqual(labels('https://x.com/jack/with_replies'), ['@jack', 'Replies']);
|
|
assert.deepEqual(labels('https://x.com/jack/media'), ['@jack', 'Media']);
|
|
assert.deepEqual(labels('https://x.com/jack/likes'), ['@jack', 'Likes']);
|
|
assert.deepEqual(labels('https://x.com/jack/followers'), ['@jack', 'Followers']);
|
|
assert.deepEqual(labels('https://x.com/jack/following'), ['@jack', 'Following']);
|
|
});
|
|
|
|
test('twitter.com behaves the same as x.com', () => {
|
|
assert.deepEqual(labels('https://twitter.com/home'), labels('https://x.com/home'));
|
|
assert.deepEqual(labels('https://twitter.com/jack/status/42'), labels('https://x.com/jack/status/42'));
|
|
});
|
|
|
|
test('site chip label follows wording setting', () => {
|
|
assert.equal(parseAddress('https://x.com/home', 'x')!.site.label, 'X');
|
|
assert.equal(parseAddress('https://x.com/home', 'twitter')!.site.label, 'Twitter');
|
|
});
|
|
|
|
test('http url is flagged insecure', () => {
|
|
const model = parseAddress('http://x.com/home', 'x');
|
|
assert.ok(model);
|
|
assert.equal(model!.insecure, true);
|
|
assert.equal(parseAddress('https://x.com/home', 'x')!.insecure, false);
|
|
});
|
|
|
|
test('other hosts show hostname without www and truncated path parts', () => {
|
|
const model = parseAddress('https://www.example.com/some/very/long/path/segment/name/that/is/too/long', 'x');
|
|
assert.ok(model);
|
|
assert.equal(model!.site.kind, 'host');
|
|
assert.equal(model!.site.label, 'example.com');
|
|
assert.ok(model!.segments[0].label.length <= 24);
|
|
});
|
|
|
|
test('non-empty query on other hosts shows a trailing chip', () => {
|
|
const model = parseAddress('https://example.com/path?x=1', 'x');
|
|
assert.ok(model);
|
|
assert.equal(model!.segments[model!.segments.length - 1].label, '?…');
|
|
});
|
|
|
|
test('about and file schemes and invalid urls are unparsable', () => {
|
|
assert.equal(parseAddress('about:blank', 'x'), null);
|
|
assert.equal(parseAddress('file:///etc/passwd', 'x'), null);
|
|
assert.equal(parseAddress('not a url', 'x'), null);
|
|
});
|