blob: e88ed63e507f4c5992113e2273ca323b6e92ffb3 [file]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@TestOn('browser')
import 'package:html_shim/html.dart';
import 'package:devtools_app/src/table_data.dart';
import 'package:devtools_app/src/html_tables.dart';
import 'package:test/test.dart';
void main() {
final List<TestData> oneThousandRows =
List<TestData>.generate(1000, (int i) => TestData('Test Data $i'));
group('static tables', () {
HtmlTable<TestData> table;
setUp(() async {
table = HtmlTable<TestData>();
// About 10 rows of data visible.
table.element.element.style
..height = '300px'
..overflow = 'scroll';
document.body.append(table.element.element);
table.model
..addColumn(TestColumn('Col One'))
..addColumn(TestColumn('Col Two'))
..setRows(oneThousandRows);
await window.animationFrame;
});
tearDown(() => table?.element?.element?.remove());
test('render all rows even when only a subset is visible', () async {
// Expect 1001 due to spacer row.
expect(table.element.element.querySelectorAll('tr').length, equals(1001));
});
});
group('virtual tables', () {
HtmlTable<TestData> table;
setUp(() async {
table = HtmlTable<TestData>.virtual();
// About 10 rows of data visible.
table.element.element.style
..height = '300px'
..overflow = 'scroll';
document.body.append(table.element.element);
table.model
..addColumn(TestColumn('Col One'))
..addColumn(TestColumn('Col Two'))
..setRows(oneThousandRows);
await window.animationFrame;
});
tearDown(() => table?.element?.element?.remove());
test('render only a small number of rows', () async {
expect(
table.element.element.querySelectorAll('tr').length, lessThan(1100));
});
test('render rows starting around 0 when not scrolled', () async {
final int rowNumber = getApproximatelyFirstRenderedDataIndex(table);
expect(rowNumber, lessThan(5));
});
test('can selected by index', () async {
final Element tbody = table.element.element.querySelector('tbody');
table.selectByIndex(0);
// Ensure a single visible row is marked as selected.
expect(tbody.querySelectorAll('tr.selected'), hasLength(1));
});
test('can select an offscreen row then scroll it into view', () async {
final Element tbody = table.element.element.querySelector('tbody');
// Select a row that will be offscreen.
table.selectByIndex(500, keepVisible: false);
// Ensure there are no selected rows.
expect(tbody.querySelectorAll('tr.selected'), isEmpty);
// Scroll to approx row 500.
table.element.scrollTop = 29 * 500;
// Wait for two frames, to ensure that the onScroll fired and then we
// definitely rebuilt the table.
await window.animationFrame;
await window.animationFrame;
// Ensure there is now a single visible row marked as selected.
expect(tbody.querySelectorAll('tr.selected'), hasLength(1));
});
test('scrolls to the selection automatically', () async {
final Element tbody = table.element.element.querySelector('tbody');
// Select a row that is offscreen.
table.selectByIndex(500, scrollBehavior: 'instant');
// Wait for two frames, to ensure that the onScroll fired and then we
// definitely rebuilt the table.
await window.animationFrame;
await window.animationFrame;
// Ensure there is now a single visible row marked as selected.
expect(tbody.querySelectorAll('tr.selected'), hasLength(1));
final int rowNumber = getApproximatelyFirstRenderedDataIndex(table);
expect(rowNumber, greaterThan(450));
expect(rowNumber, lessThan(550));
});
test('render rows starting around 500 when scrolled down the page',
() async {
// Scroll to approx row 500.
table.element.scrollTop = 29 * 500;
// Wait for two frames, to ensure that the onScroll fired and then we
// definitely rebuilt the table.
await window.animationFrame;
await window.animationFrame;
final int rowNumber = getApproximatelyFirstRenderedDataIndex(table);
expect(rowNumber, greaterThan(450));
expect(rowNumber, lessThan(550));
});
});
}
int getApproximatelyFirstRenderedDataIndex(HtmlTable<TestData> table) {
// It's possible we have a spacer row and a dummy row to force the alternating
// colour to line up, so look at the third row (index: 2) to ensure it's
// approximately what we'd expect.
final Element dataRow =
table.element.element.querySelector('tbody').children[2];
final Element cell = dataRow.querySelector('td');
expect(cell.text, contains('Test Data '));
final int rowNumber = int.tryParse(cell.text.replaceAll('Test Data ', ''));
return rowNumber;
}
class TestData {
TestData(this.message);
final String message;
}
class TestColumn extends ColumnData<TestData> {
TestColumn(String name) : super(name);
@override
dynamic getValue(TestData item) => item.message;
@override
String render(dynamic value) {
return value;
}
}