blob: 1d8b4ed87fa659e267a619767832371ba0d13ecb [file] [log] [blame]
/**
* @license
* Copyright 2022 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import * as sinon from 'sinon';
import {assert} from '@open-wc/testing';
import {DiffInfo, GrDiffLineType, Side} from '../../../api/diff';
import {getAppContext} from '../../../services/app-context';
import {
HighlightService,
highlightServiceToken,
} from '../../../services/highlight/highlight-service';
import '../../../test/common-test-setup';
import {testResolver} from '../../../test/common-test-setup';
import {mockPromise} from '../../../test/test-utils';
import {SyntaxLayerLine} from '../../../types/syntax-worker-api';
import {GrDiffLine} from '../gr-diff/gr-diff-line';
import {GrSyntaxLayerWorker} from './gr-syntax-layer-worker';
const diff: DiffInfo = {
meta_a: {
name: 'somepath/somefile.js',
content_type: 'text/javascript',
lines: 3,
language: 'lang-left',
},
meta_b: {
name: 'somepath/somefile.js',
content_type: 'text/javascript',
lines: 4,
language: 'lang-right',
},
change_type: 'MODIFIED',
intraline_status: 'OK',
content: [
{
ab: ['import it;'],
},
{
b: ['b only'],
},
{
ab: [' public static final {', 'ab3'],
},
],
};
const leftRanges: SyntaxLayerLine[] = [
{ranges: [{start: 0, length: 6, className: 'literal'}]},
{ranges: []},
{ranges: []},
];
const rightRanges: SyntaxLayerLine[] = [
{ranges: []},
{ranges: []},
{
ranges: [
{start: 0, length: 2, className: 'not-safe'},
{start: 2, length: 6, className: 'literal'},
{start: 9, length: 6, className: 'keyword'},
{start: 16, length: 5, className: 'name'},
],
},
{ranges: []},
];
suite('gr-syntax-layer-worker tests', () => {
let layer: GrSyntaxLayerWorker;
let listener: sinon.SinonStub;
let highlightService: HighlightService;
const annotate = (side: Side, lineNumber: number, text: string) => {
const el = document.createElement('div');
const lineNumberEl = document.createElement('td');
el.setAttribute('data-side', side);
el.innerText = text;
const line = new GrDiffLine(GrDiffLineType.BOTH);
if (side === Side.LEFT) line.beforeNumber = lineNumber;
if (side === Side.RIGHT) line.afterNumber = lineNumber;
layer.annotate(el, lineNumberEl, line);
return el;
};
setup(() => {
highlightService = testResolver(highlightServiceToken);
layer = new GrSyntaxLayerWorker(
() => highlightService,
() => getAppContext().reportingService
);
});
test('cancel processing', async () => {
const mockPromise1 = mockPromise<SyntaxLayerLine[]>();
const mockPromise2 = mockPromise<SyntaxLayerLine[]>();
const mockPromise3 = mockPromise<SyntaxLayerLine[]>();
const mockPromise4 = mockPromise<SyntaxLayerLine[]>();
const stub = sinon.stub(highlightService, 'highlight');
stub.onCall(0).returns(mockPromise1);
stub.onCall(1).returns(mockPromise2);
stub.onCall(2).returns(mockPromise3);
stub.onCall(3).returns(mockPromise4);
const processPromise1 = layer.process(diff);
// Calling the process() a second time means that the promises created
// during the first call are cancelled.
const processPromise2 = layer.process(diff);
// We can await the outer promise even before the inner promises resolve,
// because cancelling rejects the inner promises.
await processPromise1;
// It does not matter actually, whether these two inner promises are
// resolved or not.
mockPromise1.resolve(leftRanges);
mockPromise2.resolve(rightRanges);
// Both ranges must still be empty, because the promise of the first call
// must have been cancelled and the returned ranges ignored.
assert.isEmpty(layer.leftRanges);
assert.isEmpty(layer.rightRanges);
// Lets' resolve and await the promises of the second as normal.
mockPromise3.resolve(leftRanges);
mockPromise4.resolve(rightRanges);
await processPromise2;
assert.equal(layer.leftRanges, leftRanges);
});
suite('annotate and listen', () => {
setup(() => {
listener = sinon.stub();
layer.addListener(listener);
sinon.stub(highlightService, 'highlight').callsFake((lang?: string) => {
if (lang === 'lang-left') return Promise.resolve(leftRanges);
if (lang === 'lang-right') return Promise.resolve(rightRanges);
return Promise.resolve([]);
});
});
test('process and annotate line 2 LEFT', async () => {
await layer.process(diff);
const el = annotate(Side.LEFT, 1, 'import it;');
assert.equal(
el.innerHTML,
'<hl class="gr-syntax gr-syntax-literal">import</hl> it;'
);
assert.equal(listener.callCount, 2);
assert.equal(listener.getCall(0).args[0], 1);
assert.equal(listener.getCall(0).args[1], 1);
assert.equal(listener.getCall(0).args[2], Side.LEFT);
assert.equal(listener.getCall(1).args[0], 3);
assert.equal(listener.getCall(1).args[1], 3);
assert.equal(listener.getCall(1).args[2], Side.RIGHT);
});
test('process and annotate line 3 RIGHT', async () => {
await layer.process(diff);
const el = annotate(Side.RIGHT, 3, ' public static final {');
assert.equal(
el.innerHTML,
' <hl class="gr-syntax gr-syntax-literal">public</hl> ' +
'<hl class="gr-syntax gr-syntax-keyword">static</hl> ' +
'<hl class="gr-syntax gr-syntax-name">final</hl> {'
);
assert.equal(listener.callCount, 2);
});
});
});