// Test cases for MessageList markup parsing
import Pango from 'gi://Pango';
import 'resource:///org/gnome/shell/ui/environment.js';
import {fixMarkup} from 'resource:///org/gnome/shell/misc/util.js';
describe('fixMarkup()', () => {
function convertAndEscape(text) {
return {
converted: fixMarkup(text, true),
escaped: fixMarkup(text, false),
};
}
beforeAll(() => {
jasmine.addMatchers({
toParseCorrectlyAndMatch: () => {
function isMarkupValid(markup) {
try {
Pango.parse_markup(markup, -1, '');
} catch (e) {
return false;
}
return true;
}
return {
compare: (actual, expected) => {
if (!expected)
expected = actual;
return {
pass: isMarkupValid(actual) && actual === expected,
message: `Expected "${actual}" to parse correctly and equal "${expected}"`,
};
},
};
},
});
});
it('does not do anything on no markup', () => {
const text = 'foo';
const result = convertAndEscape(text);
expect(result.converted).toParseCorrectlyAndMatch(text);
expect(result.escaped).toParseCorrectlyAndMatch(text);
});
it('converts and escapes bold markup', () => {
const text = 'foo';
const {converted, escaped} = convertAndEscape(text);
expect(converted).toParseCorrectlyAndMatch();
expect(escaped).toParseCorrectlyAndMatch('<b>foo</b>');
});
it('converts and escapes italic markup', () => {
const text = 'something foo';
const {converted, escaped} = convertAndEscape(text);
expect(converted).toParseCorrectlyAndMatch();
expect(escaped).toParseCorrectlyAndMatch('something <i>foo</i>');
});
it('converts and escapes underlined markup', () => {
const text = 'foo something';
const {converted, escaped} = convertAndEscape(text);
expect(converted).toParseCorrectlyAndMatch();
expect(escaped).toParseCorrectlyAndMatch('<u>foo</u> something');
});
it('converts and escapes non-nested bold italic and underline markup', () => {
const text = 'bold italic and underlined';
const {converted, escaped} = convertAndEscape(text);
expect(converted).toParseCorrectlyAndMatch();
expect(escaped).toParseCorrectlyAndMatch('<b>bold</b> <i>italic <u>and underlined</u></i>');
});
it('converts and escapes ampersands', () => {
const text = 'this & that';
const {converted, escaped} = convertAndEscape(text);
expect(converted).toParseCorrectlyAndMatch();
expect(escaped).toParseCorrectlyAndMatch('this & that');
});
it('converts and escapes <', () => {
const text = 'this < that';
const {converted, escaped} = convertAndEscape(text);
expect(converted).toParseCorrectlyAndMatch();
expect(escaped).toParseCorrectlyAndMatch('this < that');
});
it('converts and escapes >', () => {
const text = 'this < that > the other';
const {converted, escaped} = convertAndEscape(text);
expect(converted).toParseCorrectlyAndMatch();
expect(escaped).toParseCorrectlyAndMatch('this < that > the other');
});
it('converts and escapes HTML markup inside escaped tags', () => {
const text = 'this <that>';
const {converted, escaped} = convertAndEscape(text);
expect(converted).toParseCorrectlyAndMatch();
expect(escaped).toParseCorrectlyAndMatch('this <<i>that</i>>');
});
it('converts and escapes angle brackets within HTML markup', () => {
const text = 'this > that';
const {converted, escaped} = convertAndEscape(text);
expect(converted).toParseCorrectlyAndMatch();
expect(escaped).toParseCorrectlyAndMatch('<b>this</b> > <i>that</i>');
});
it('converts and escapes markup whilst still keeping an unrecognized entity', () => {
const text = 'smile ☺!';
const {converted, escaped} = convertAndEscape(text);
expect(converted).toParseCorrectlyAndMatch();
expect(escaped).toParseCorrectlyAndMatch('<b>smile</b> ☺!');
});
it('converts and escapes markup and a stray ampersand', () => {
const text = 'this & that';
const {converted, escaped} = convertAndEscape(text);
expect(converted).toParseCorrectlyAndMatch('this & that');
expect(escaped).toParseCorrectlyAndMatch('<b>this</b> & <i>that</i>');
});
it('converts and escapes a stray <', () => {
const text = 'this < that';
const {converted, escaped} = convertAndEscape(text);
expect(converted).toParseCorrectlyAndMatch('this < that');
expect(escaped).toParseCorrectlyAndMatch('this < that');
});
it('converts and escapes markup with a stray <', () => {
const text = 'this < that';
const {converted, escaped} = convertAndEscape(text);
expect(converted).toParseCorrectlyAndMatch('this < that');
expect(escaped).toParseCorrectlyAndMatch('<b>this</b> < <i>that</i>');
});
it('converts and escapes stray less than and greater than characters that do not form tags', () => {
const text = 'this < that > the other';
const {converted, escaped} = convertAndEscape(text);
expect(converted).toParseCorrectlyAndMatch('this < that > the other');
expect(escaped).toParseCorrectlyAndMatch('this < that > the other');
});
it('converts and escapes stray less than and greater than characters next to HTML markup tags', () => {
const text = 'this <that>';
const {converted, escaped} = convertAndEscape(text);
expect(converted).toParseCorrectlyAndMatch('this <that>');
expect(escaped).toParseCorrectlyAndMatch('this <<i>that</i>>');
});
it('converts and escapes angle brackets around unknown tags', () => {
const text = 'tag';
const {converted, escaped} = convertAndEscape(text);
expect(converted).toParseCorrectlyAndMatch('<unknown>tag</unknown>');
expect(escaped).toParseCorrectlyAndMatch('<unknown>tag</unknown>');
});
it('converts and escapes angle brackets around unknown tags where the first letter might otherwise be valid HTML markup', () => {
const text = 'tag';
const {converted, escaped} = convertAndEscape(text);
expect(converted).toParseCorrectlyAndMatch('<bunknown>tag</bunknown>');
expect(escaped).toParseCorrectlyAndMatch('<bunknown>tag</bunknown>');
});
it('converts good tags but escapes bad tags', () => {
const text = 'known and tag';
const {converted, escaped} = convertAndEscape(text);
expect(converted).toParseCorrectlyAndMatch('known and <unknown>tag</unknown>');
expect(escaped).toParseCorrectlyAndMatch('<i>known</i> and <unknown>tag</unknown>');
});
it('completely escapes mismatched tags where the mismatch is at the beginning', () => {
const text = 'incomplete';
const {converted, escaped} = convertAndEscape(text);
expect(converted).toParseCorrectlyAndMatch('<b>in<i>com</i>plete');
expect(escaped).toParseCorrectlyAndMatch('<b>in<i>com</i>plete');
});
it('completely escapes mismatched tags where the mismatch is at the end', () => {
const text = 'incomplete';
const {converted, escaped} = convertAndEscape(text);
expect(converted).toParseCorrectlyAndMatch('in<i>com</i>plete</b>');
expect(escaped).toParseCorrectlyAndMatch('in<i>com</i>plete</b>');
});
it('escapes all tags where there are attributes', () => {
const text = 'good and bad';
const {converted, escaped} = convertAndEscape(text);
expect(converted).toParseCorrectlyAndMatch('<b>good</b> and <b style='bad'>bad</b>');
expect(escaped).toParseCorrectlyAndMatch('<b>good</b> and <b style='bad'>bad</b>');
});
it('escapes all tags where syntax is invalid', () => {
const text = 'unrecognized';
const {converted, escaped} = convertAndEscape(text);
expect(converted).toParseCorrectlyAndMatch('<b>unrecognized</b stuff>');
expect(escaped).toParseCorrectlyAndMatch('<b>unrecognized</b stuff>');
});
it('escapes completely mismatched tags', () => {
const text = 'mismatched';
const {converted, escaped} = convertAndEscape(text);
expect(converted).toParseCorrectlyAndMatch('<b>mismatched</i>');
expect(escaped).toParseCorrectlyAndMatch('<b>mismatched</i>');
});
it('escapes mismatched tags where the first character is mismatched', () => {
const text = 'mismatched/unknown';
const {converted, escaped} = convertAndEscape(text);
expect(converted).toParseCorrectlyAndMatch('<b>mismatched/unknown</bunknown>');
expect(escaped).toParseCorrectlyAndMatch('<b>mismatched/unknown</bunknown>');
});
});