1
0
mirror of https://github.com/flutter/samples.git synced 2025-11-12 07:48:55 +00:00

Add flutter_web samples (#75)

This commit is contained in:
Kevin Moore
2019-05-07 13:32:08 -07:00
committed by Andrew Brogdon
parent 42f2dce01b
commit 3fe927cb29
697 changed files with 241026 additions and 0 deletions

View File

@@ -0,0 +1,644 @@
// Copyright 2018 the Charts project authors. Please see the AUTHORS file
// for details.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import 'package:charts_common/src/chart/line/line_renderer.dart';
import 'package:charts_common/src/chart/line/line_renderer_config.dart';
import 'package:charts_common/src/chart/common/processed_series.dart'
show MutableSeries, ImmutableSeries;
import 'package:charts_common/src/common/color.dart';
import 'package:charts_common/src/common/material_palette.dart'
show MaterialPalette;
import 'package:charts_common/src/data/series.dart' show Series;
import 'package:mockito/mockito.dart';
import 'package:test/test.dart';
/// Datum/Row for the chart.
class MyRow {
final String campaignString;
final int campaign;
final int clickCount;
final Color color;
final List<int> dashPattern;
final double strokeWidthPx;
MyRow(this.campaignString, this.campaign, this.clickCount, this.color,
this.dashPattern, this.strokeWidthPx);
}
class MockImmutableSeries<D> extends Mock implements ImmutableSeries<D> {
String _id;
MockImmutableSeries(this._id);
@override
String get id => _id;
}
void main() {
LineRenderer renderer;
List<MutableSeries<int>> numericSeriesList;
List<MutableSeries<String>> ordinalSeriesList;
List<MyRow> myFakeDesktopData;
List<MyRow> myFakeTabletData;
List<MyRow> myFakeMobileData;
setUp(() {
myFakeDesktopData = [
new MyRow(
'MyCampaign1', 1, 5, MaterialPalette.blue.shadeDefault, null, 2.0),
new MyRow(
'MyCampaign2', 2, 25, MaterialPalette.green.shadeDefault, null, 2.0),
new MyRow(
'MyCampaign3', 3, 100, MaterialPalette.red.shadeDefault, null, 2.0),
new MyRow('MyOtherCampaign', 4, 75, MaterialPalette.red.shadeDefault,
null, 2.0),
];
myFakeTabletData = [
new MyRow(
'MyCampaign1', 1, 5, MaterialPalette.blue.shadeDefault, [2, 2], 2.0),
new MyRow(
'MyCampaign2', 2, 25, MaterialPalette.blue.shadeDefault, [3, 3], 2.0),
new MyRow('MyCampaign3', 3, 100, MaterialPalette.blue.shadeDefault,
[4, 4], 2.0),
new MyRow('MyOtherCampaign', 4, 75, MaterialPalette.blue.shadeDefault,
[4, 4], 2.0),
];
myFakeMobileData = [
new MyRow(
'MyCampaign1', 1, 5, MaterialPalette.blue.shadeDefault, null, 2.0),
new MyRow(
'MyCampaign2', 2, 25, MaterialPalette.blue.shadeDefault, null, 3.0),
new MyRow(
'MyCampaign3', 3, 100, MaterialPalette.blue.shadeDefault, null, 4.0),
new MyRow('MyOtherCampaign', 4, 75, MaterialPalette.blue.shadeDefault,
null, 4.0),
];
numericSeriesList = [
new MutableSeries<int>(new Series<MyRow, int>(
id: 'Desktop',
colorFn: (_, __) => MaterialPalette.blue.shadeDefault,
domainFn: (dynamic row, _) => row.campaign,
measureFn: (dynamic row, _) => row.clickCount,
measureOffsetFn: (_, __) => 0,
data: myFakeDesktopData)),
new MutableSeries<int>(new Series<MyRow, int>(
id: 'Tablet',
colorFn: (_, __) => MaterialPalette.red.shadeDefault,
domainFn: (dynamic row, _) => row.campaign,
measureFn: (dynamic row, _) => row.clickCount,
measureOffsetFn: (_, __) => 0,
strokeWidthPxFn: (_, __) => 1.25,
data: myFakeTabletData)),
new MutableSeries<int>(new Series<MyRow, int>(
id: 'Mobile',
colorFn: (_, __) => MaterialPalette.green.shadeDefault,
domainFn: (dynamic row, _) => row.campaign,
measureFn: (dynamic row, _) => row.clickCount,
measureOffsetFn: (_, __) => 0,
strokeWidthPxFn: (_, __) => 3.0,
data: myFakeMobileData))
];
ordinalSeriesList = [
new MutableSeries<String>(new Series<MyRow, String>(
id: 'Desktop',
colorFn: (_, __) => MaterialPalette.blue.shadeDefault,
domainFn: (dynamic row, _) => row.campaignString,
measureFn: (dynamic row, _) => row.clickCount,
measureOffsetFn: (_, __) => 0,
data: myFakeDesktopData)),
new MutableSeries<String>(new Series<MyRow, String>(
id: 'Tablet',
colorFn: (_, __) => MaterialPalette.red.shadeDefault,
domainFn: (dynamic row, _) => row.campaignString,
measureFn: (dynamic row, _) => row.clickCount,
measureOffsetFn: (_, __) => 0,
strokeWidthPxFn: (_, __) => 1.25,
data: myFakeTabletData)),
new MutableSeries<String>(new Series<MyRow, String>(
id: 'Mobile',
colorFn: (_, __) => MaterialPalette.green.shadeDefault,
domainFn: (dynamic row, _) => row.campaignString,
measureFn: (dynamic row, _) => row.clickCount,
measureOffsetFn: (_, __) => 0,
strokeWidthPxFn: (_, __) => 3.0,
data: myFakeMobileData))
];
});
group('preprocess', () {
test('with numeric data and simple lines', () {
renderer = new LineRenderer<num>(
config: new LineRendererConfig(strokeWidthPx: 2.0));
renderer.configureSeries(numericSeriesList);
renderer.preprocessSeries(numericSeriesList);
expect(numericSeriesList.length, equals(3));
// Validate Desktop series.
var series = numericSeriesList[0];
var styleSegments = series.getAttr(styleSegmentsKey);
expect(styleSegments.length, equals(1));
var segment = styleSegments[0];
expect(segment.color, equals(MaterialPalette.blue.shadeDefault));
expect(segment.dashPattern, isNull);
expect(segment.domainExtent.start, equals(1));
expect(segment.domainExtent.end, equals(4));
expect(segment.strokeWidthPx, equals(2.0));
expect(series.measureOffsetFn(0), 0);
expect(series.measureOffsetFn(1), 0);
expect(series.measureOffsetFn(2), 0);
expect(series.measureOffsetFn(3), 0);
// Validate Tablet series.
series = numericSeriesList[1];
styleSegments = series.getAttr(styleSegmentsKey);
expect(styleSegments.length, equals(1));
segment = styleSegments[0];
expect(segment.color, equals(MaterialPalette.red.shadeDefault));
expect(segment.dashPattern, isNull);
expect(segment.domainExtent.start, equals(1));
expect(segment.domainExtent.end, equals(4));
expect(segment.strokeWidthPx, equals(1.25));
expect(series.measureOffsetFn(0), 0);
expect(series.measureOffsetFn(1), 0);
expect(series.measureOffsetFn(2), 0);
expect(series.measureOffsetFn(3), 0);
// Validate Mobile series.
series = numericSeriesList[2];
styleSegments = series.getAttr(styleSegmentsKey);
expect(styleSegments.length, equals(1));
segment = styleSegments[0];
expect(segment.color, equals(MaterialPalette.green.shadeDefault));
expect(segment.dashPattern, isNull);
expect(segment.domainExtent.start, equals(1));
expect(segment.domainExtent.end, equals(4));
expect(segment.strokeWidthPx, equals(3.0));
expect(series.measureOffsetFn(0), 0);
expect(series.measureOffsetFn(1), 0);
expect(series.measureOffsetFn(2), 0);
expect(series.measureOffsetFn(3), 0);
});
test('with numeric data and stacked lines', () {
renderer = new LineRenderer<num>(
config: new LineRendererConfig(stacked: true, strokeWidthPx: 2.0));
renderer.configureSeries(numericSeriesList);
renderer.preprocessSeries(numericSeriesList);
expect(numericSeriesList.length, equals(3));
// Validate Desktop series.
var series = numericSeriesList[0];
var styleSegments = series.getAttr(styleSegmentsKey);
expect(styleSegments.length, equals(1));
var segment = styleSegments[0];
expect(segment.color, equals(MaterialPalette.blue.shadeDefault));
expect(segment.dashPattern, isNull);
expect(segment.domainExtent.start, equals(1));
expect(segment.domainExtent.end, equals(4));
expect(segment.strokeWidthPx, equals(2.0));
expect(series.measureOffsetFn(0), 0);
expect(series.measureOffsetFn(1), 0);
expect(series.measureOffsetFn(2), 0);
expect(series.measureOffsetFn(3), 0);
// Validate Tablet series.
series = numericSeriesList[1];
styleSegments = series.getAttr(styleSegmentsKey);
expect(styleSegments.length, equals(1));
segment = styleSegments[0];
expect(segment.color, equals(MaterialPalette.red.shadeDefault));
expect(segment.dashPattern, isNull);
expect(segment.domainExtent.start, equals(1));
expect(segment.domainExtent.end, equals(4));
expect(segment.strokeWidthPx, equals(1.25));
expect(series.measureOffsetFn(0), 5);
expect(series.measureOffsetFn(1), 25);
expect(series.measureOffsetFn(2), 100);
expect(series.measureOffsetFn(3), 75);
// Validate Mobile series.
series = numericSeriesList[2];
styleSegments = series.getAttr(styleSegmentsKey);
expect(styleSegments.length, equals(1));
segment = styleSegments[0];
expect(segment.color, equals(MaterialPalette.green.shadeDefault));
expect(segment.dashPattern, isNull);
expect(segment.domainExtent.start, equals(1));
expect(segment.domainExtent.end, equals(4));
expect(segment.strokeWidthPx, equals(3.0));
expect(series.measureOffsetFn(0), 10);
expect(series.measureOffsetFn(1), 50);
expect(series.measureOffsetFn(2), 200);
expect(series.measureOffsetFn(3), 150);
});
test('with numeric data and changes in style', () {
numericSeriesList = [
new MutableSeries<int>(new Series<MyRow, int>(
id: 'Desktop',
colorFn: (MyRow row, _) => row.color,
dashPatternFn: (MyRow row, _) => row.dashPattern,
strokeWidthPxFn: (MyRow row, _) => row.strokeWidthPx,
domainFn: (dynamic row, _) => row.campaign,
measureFn: (dynamic row, _) => row.clickCount,
measureOffsetFn: (_, __) => 0,
data: myFakeDesktopData)),
new MutableSeries<int>(new Series<MyRow, int>(
id: 'Tablet',
colorFn: (MyRow row, _) => row.color,
dashPatternFn: (MyRow row, _) => row.dashPattern,
strokeWidthPxFn: (MyRow row, _) => row.strokeWidthPx,
domainFn: (dynamic row, _) => row.campaign,
measureFn: (dynamic row, _) => row.clickCount,
measureOffsetFn: (_, __) => 0,
data: myFakeTabletData)),
new MutableSeries<int>(new Series<MyRow, int>(
id: 'Mobile',
colorFn: (MyRow row, _) => row.color,
dashPatternFn: (MyRow row, _) => row.dashPattern,
strokeWidthPxFn: (MyRow row, _) => row.strokeWidthPx,
domainFn: (dynamic row, _) => row.campaign,
measureFn: (dynamic row, _) => row.clickCount,
measureOffsetFn: (_, __) => 0,
data: myFakeMobileData))
];
renderer = new LineRenderer<num>(
config: new LineRendererConfig(strokeWidthPx: 2.0));
renderer.configureSeries(numericSeriesList);
renderer.preprocessSeries(numericSeriesList);
expect(numericSeriesList.length, equals(3));
// Validate Desktop series.
var series = numericSeriesList[0];
var styleSegments = series.getAttr(styleSegmentsKey);
expect(styleSegments.length, equals(3));
var segment = styleSegments[0];
expect(segment.color, equals(MaterialPalette.blue.shadeDefault));
expect(segment.dashPattern, isNull);
expect(segment.domainExtent.start, equals(1));
expect(segment.domainExtent.end, equals(2));
expect(segment.strokeWidthPx, equals(2.0));
segment = styleSegments[1];
expect(segment.color, equals(MaterialPalette.green.shadeDefault));
expect(segment.dashPattern, isNull);
expect(segment.domainExtent.start, equals(2));
expect(segment.domainExtent.end, equals(3));
expect(segment.strokeWidthPx, equals(2.0));
segment = styleSegments[2];
expect(segment.color, equals(MaterialPalette.red.shadeDefault));
expect(segment.dashPattern, isNull);
expect(segment.domainExtent.start, equals(3));
expect(segment.domainExtent.end, equals(4));
expect(segment.strokeWidthPx, equals(2.0));
expect(series.measureOffsetFn(0), 0);
expect(series.measureOffsetFn(1), 0);
expect(series.measureOffsetFn(2), 0);
expect(series.measureOffsetFn(3), 0);
// Validate Tablet series.
series = numericSeriesList[1];
styleSegments = series.getAttr(styleSegmentsKey);
expect(styleSegments.length, equals(3));
segment = segment = styleSegments[0];
expect(segment.color, equals(MaterialPalette.blue.shadeDefault));
expect(segment.dashPattern, equals([2, 2]));
expect(segment.domainExtent.start, equals(1));
expect(segment.domainExtent.end, equals(2));
expect(segment.strokeWidthPx, equals(2.0));
segment = styleSegments[1];
expect(segment.color, equals(MaterialPalette.blue.shadeDefault));
expect(segment.dashPattern, equals([3, 3]));
expect(segment.domainExtent.start, equals(2));
expect(segment.domainExtent.end, equals(3));
expect(segment.strokeWidthPx, equals(2.0));
segment = styleSegments[2];
expect(segment.color, equals(MaterialPalette.blue.shadeDefault));
expect(segment.dashPattern, equals([4, 4]));
expect(segment.domainExtent.start, equals(3));
expect(segment.domainExtent.end, equals(4));
expect(segment.strokeWidthPx, equals(2.0));
expect(series.measureOffsetFn(0), 0);
expect(series.measureOffsetFn(1), 0);
expect(series.measureOffsetFn(2), 0);
expect(series.measureOffsetFn(3), 0);
// Validate Mobile series.
series = numericSeriesList[2];
styleSegments = series.getAttr(styleSegmentsKey);
expect(styleSegments.length, equals(3));
segment = segment = styleSegments[0];
expect(segment.color, equals(MaterialPalette.blue.shadeDefault));
expect(segment.dashPattern, isNull);
expect(segment.domainExtent.start, equals(1));
expect(segment.domainExtent.end, equals(2));
expect(segment.strokeWidthPx, equals(2.0));
segment = styleSegments[1];
expect(segment.color, equals(MaterialPalette.blue.shadeDefault));
expect(segment.dashPattern, isNull);
expect(segment.domainExtent.start, equals(2));
expect(segment.domainExtent.end, equals(3));
expect(segment.strokeWidthPx, equals(3.0));
segment = styleSegments[2];
expect(segment.color, equals(MaterialPalette.blue.shadeDefault));
expect(segment.dashPattern, isNull);
expect(segment.domainExtent.start, equals(3));
expect(segment.domainExtent.end, equals(4));
expect(segment.strokeWidthPx, equals(4.0));
expect(series.measureOffsetFn(0), 0);
expect(series.measureOffsetFn(1), 0);
expect(series.measureOffsetFn(2), 0);
expect(series.measureOffsetFn(3), 0);
});
test('with numeric data and repeats in style', () {
var myFakeData = [
new MyRow(
'MyCampaign1', 1, 5, MaterialPalette.blue.shadeDefault, null, 2.0),
new MyRow('MyCampaign2', 2, 25, MaterialPalette.green.shadeDefault,
null, 2.0),
new MyRow('MyCampaign3', 3, 100, MaterialPalette.blue.shadeDefault,
null, 2.0),
new MyRow('MyCampaign4', 4, 75, MaterialPalette.green.shadeDefault,
null, 2.0),
new MyRow(
'MyCampaign1', 5, 5, MaterialPalette.blue.shadeDefault, null, 2.0),
new MyRow('MyCampaign2', 6, 25, MaterialPalette.green.shadeDefault,
null, 2.0),
new MyRow('MyCampaign3', 7, 100, MaterialPalette.blue.shadeDefault,
null, 2.0),
new MyRow('MyCampaign4', 8, 75, MaterialPalette.green.shadeDefault,
null, 2.0),
];
numericSeriesList = [
new MutableSeries<int>(new Series<MyRow, int>(
id: 'Desktop',
colorFn: (MyRow row, _) => row.color,
dashPatternFn: (MyRow row, _) => row.dashPattern,
strokeWidthPxFn: (MyRow row, _) => row.strokeWidthPx,
domainFn: (dynamic row, _) => row.campaign,
measureFn: (dynamic row, _) => row.clickCount,
measureOffsetFn: (_, __) => 0,
data: myFakeData)),
];
renderer = new LineRenderer<num>(
config: new LineRendererConfig(strokeWidthPx: 2.0));
renderer.configureSeries(numericSeriesList);
renderer.preprocessSeries(numericSeriesList);
expect(numericSeriesList.length, equals(1));
// Validate Desktop series.
var series = numericSeriesList[0];
var styleSegments = series.getAttr(styleSegmentsKey);
expect(styleSegments.length, equals(8));
var segment = styleSegments[0];
expect(segment.color, equals(MaterialPalette.blue.shadeDefault));
expect(segment.domainExtent.start, equals(1));
expect(segment.domainExtent.end, equals(2));
segment = styleSegments[1];
expect(segment.color, equals(MaterialPalette.green.shadeDefault));
expect(segment.domainExtent.start, equals(2));
expect(segment.domainExtent.end, equals(3));
segment = styleSegments[2];
expect(segment.color, equals(MaterialPalette.blue.shadeDefault));
expect(segment.domainExtent.start, equals(3));
expect(segment.domainExtent.end, equals(4));
segment = styleSegments[3];
expect(segment.color, equals(MaterialPalette.green.shadeDefault));
expect(segment.domainExtent.start, equals(4));
expect(segment.domainExtent.end, equals(5));
segment = styleSegments[4];
expect(segment.color, equals(MaterialPalette.blue.shadeDefault));
expect(segment.domainExtent.start, equals(5));
expect(segment.domainExtent.end, equals(6));
segment = styleSegments[5];
expect(segment.color, equals(MaterialPalette.green.shadeDefault));
expect(segment.domainExtent.start, equals(6));
expect(segment.domainExtent.end, equals(7));
segment = styleSegments[6];
expect(segment.color, equals(MaterialPalette.blue.shadeDefault));
expect(segment.domainExtent.start, equals(7));
expect(segment.domainExtent.end, equals(8));
segment = styleSegments[7];
expect(segment.color, equals(MaterialPalette.green.shadeDefault));
expect(segment.domainExtent.start, equals(8));
expect(segment.domainExtent.end, equals(8));
});
test('with ordinal data and simple lines', () {
renderer = new LineRenderer<String>(
config: new LineRendererConfig(strokeWidthPx: 2.0));
renderer.configureSeries(ordinalSeriesList);
renderer.preprocessSeries(ordinalSeriesList);
expect(ordinalSeriesList.length, equals(3));
// Validate Desktop series.
var series = ordinalSeriesList[0];
var styleSegments = series.getAttr(styleSegmentsKey);
expect(styleSegments.length, equals(1));
var segment = styleSegments[0];
expect(segment.color, equals(MaterialPalette.blue.shadeDefault));
expect(segment.dashPattern, isNull);
expect(segment.domainExtent.start, equals('MyCampaign1'));
expect(segment.domainExtent.end, equals('MyOtherCampaign'));
expect(segment.strokeWidthPx, equals(2.0));
// Validate Tablet series.
series = ordinalSeriesList[1];
styleSegments = series.getAttr(styleSegmentsKey);
expect(styleSegments.length, equals(1));
segment = styleSegments[0];
expect(segment.color, equals(MaterialPalette.red.shadeDefault));
expect(segment.dashPattern, isNull);
expect(segment.domainExtent.start, equals('MyCampaign1'));
expect(segment.domainExtent.end, equals('MyOtherCampaign'));
expect(segment.strokeWidthPx, equals(1.25));
// Validate Mobile series.
series = ordinalSeriesList[2];
styleSegments = series.getAttr(styleSegmentsKey);
expect(styleSegments.length, equals(1));
segment = styleSegments[0];
expect(segment.color, equals(MaterialPalette.green.shadeDefault));
expect(segment.dashPattern, isNull);
expect(segment.domainExtent.start, equals('MyCampaign1'));
expect(segment.domainExtent.end, equals('MyOtherCampaign'));
expect(segment.strokeWidthPx, equals(3.0));
});
});
group('Line merging', () {
List<ImmutableSeries<num>> series(List<String> keys) {
return keys.map((key) => MockImmutableSeries<num>(key)).toList();
}
test('simple beginning removal', () {
final tester = LineRendererTester(LineRenderer<num>());
tester.setSeriesKeys(['a', 'b', 'c']);
tester.merge(series(['b', 'c']));
// The series should still be there so that it can be animated out.
expect(tester.seriesKeys, equals(['a', 'b', 'c']));
});
test('simple middle removal', () {
final tester = LineRendererTester(LineRenderer<num>());
tester.setSeriesKeys(['a', 'b', 'c']);
tester.merge(series(['a', 'c']));
// The series should still be there so that it can be animated out.
expect(tester.seriesKeys, equals(['a', 'b', 'c']));
});
test('simple end removal', () {
final tester = LineRendererTester(LineRenderer<num>());
tester.setSeriesKeys(['a', 'b', 'c']);
tester.merge(series(['a', 'b']));
// The series should still be there so that it can be animated out.
expect(tester.seriesKeys, equals(['a', 'b', 'c']));
});
test('simple beginning addition', () {
final tester = LineRendererTester(LineRenderer<num>());
tester.setSeriesKeys(['a', 'b', 'c']);
tester.merge(series(['d', 'a', 'b', 'c']));
expect(tester.seriesKeys, equals(['d', 'a', 'b', 'c']));
});
test('simple middle addition', () {
final tester = LineRendererTester(LineRenderer<num>());
tester.setSeriesKeys(['a', 'b', 'c']);
tester.merge(series(['a', 'd', 'b', 'c']));
expect(tester.seriesKeys, equals(['a', 'd', 'b', 'c']));
});
test('simple end addition', () {
final tester = LineRendererTester(LineRenderer<num>());
tester.setSeriesKeys(['a', 'b', 'c']);
tester.merge(series(['a', 'b', 'c', 'd']));
expect(tester.seriesKeys, equals(['a', 'b', 'c', 'd']));
});
test('replacement begining', () {
final tester = LineRendererTester(LineRenderer<num>());
tester.setSeriesKeys(['a', 'b', 'c']);
tester.merge(series(['d', 'b', 'c']));
expect(tester.seriesKeys, equals(['a', 'd', 'b', 'c']));
});
test('replacement end', () {
final tester = LineRendererTester(LineRenderer<num>());
tester.setSeriesKeys(['a', 'b', 'c']);
tester.merge(series(['a', 'b', 'd']));
expect(tester.seriesKeys, equals(['a', 'b', 'c', 'd']));
});
test('full replacement', () {
final tester = LineRendererTester(LineRenderer<num>());
tester.setSeriesKeys(['a', 'b', 'c']);
tester.merge(series(['d', 'e', 'f']));
expect(tester.seriesKeys, equals(['a', 'b', 'c', 'd', 'e', 'f']));
});
test('mixed replacement', () {
final tester = LineRendererTester(LineRenderer<num>());
tester.setSeriesKeys(['a', 'b', 'c', 'd']);
tester.merge(series(['d', 'a', 'f', 'c']));
expect(tester.seriesKeys, equals(['d', 'a', 'b', 'f', 'c']));
});
});
}

View File

@@ -0,0 +1,354 @@
// Copyright 2018 the Charts project authors. Please see the AUTHORS file
// for details.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import 'dart:math';
import 'package:charts_common/src/chart/cartesian/axis/axis.dart';
import 'package:charts_common/src/chart/cartesian/cartesian_chart.dart';
import 'package:charts_common/src/chart/common/chart_canvas.dart';
import 'package:charts_common/src/chart/common/processed_series.dart';
import 'package:charts_common/src/chart/line/line_renderer.dart';
import 'package:charts_common/src/chart/line/line_renderer_config.dart';
import 'package:charts_common/src/common/color.dart';
import 'package:charts_common/src/data/series.dart';
import 'package:mockito/mockito.dart';
import 'package:test/test.dart';
/// Datum/Row for the chart.
class MyRow {
final int timestamp;
int clickCount;
MyRow(this.timestamp, this.clickCount);
}
// TODO: Test in RTL context as well.
class MockChart extends Mock implements CartesianChart {}
class MockDomainAxis extends Mock implements Axis<int> {}
class MockMeasureAxis extends Mock implements Axis<num> {}
class MockCanvas extends Mock implements ChartCanvas {}
void main() {
/////////////////////////////////////////
// Convenience methods for creating mocks.
/////////////////////////////////////////
MutableSeries<int> _makeSeries({String id, int measureOffset = 0}) {
final data = <MyRow>[
new MyRow(1000, measureOffset + 10),
new MyRow(2000, measureOffset + 20),
new MyRow(3000, measureOffset + 30),
];
final series = new MutableSeries<int>(new Series<MyRow, int>(
id: id,
data: data,
domainFn: (MyRow row, _) => row.timestamp,
measureFn: (MyRow row, _) => row.clickCount,
));
series.measureOffsetFn = (_) => 0.0;
series.colorFn = (_) => new Color.fromHex(code: '#000000');
// Mock the Domain axis results.
final domainAxis = new MockDomainAxis();
when(domainAxis.rangeBand).thenReturn(100.0);
when(domainAxis.getLocation(1000)).thenReturn(70.0);
when(domainAxis.getLocation(2000)).thenReturn(70.0 + 100);
when(domainAxis.getLocation(3000)).thenReturn(70.0 + 200.0);
series.setAttr(domainAxisKey, domainAxis);
// Mock the Measure axis results.
final measureAxis = new MockMeasureAxis();
for (var i = 0; i <= 100; i++) {
when(measureAxis.getLocation(i.toDouble()))
.thenReturn(20.0 + 100.0 - i.toDouble());
}
// Special case where measure is above drawArea.
when(measureAxis.getLocation(500)).thenReturn(20.0 + 100.0 - 500);
series.setAttr(measureAxisKey, measureAxis);
return series;
}
LineRenderer<int> renderer;
bool selectNearestByDomain;
setUp(() {
selectNearestByDomain = true;
renderer = new LineRenderer<int>(
config: new LineRendererConfig(strokeWidthPx: 1.0));
final layoutBounds = new Rectangle<int>(70, 20, 200, 100);
renderer.layout(layoutBounds, layoutBounds);
return renderer;
});
/////////////////////////////////////////
// Additional edge test cases
/////////////////////////////////////////
group('edge cases', () {
test('hit target with missing data in series still selects others', () {
// Setup
final seriesList = <MutableSeries<int>>[
_makeSeries(id: 'foo')..data.clear(),
_makeSeries(id: 'bar'),
];
renderer.configureSeries(seriesList);
renderer.preprocessSeries(seriesList);
renderer.update(seriesList, false);
renderer.paint(new MockCanvas(), 1.0);
// Act Point just below barSeries.data[0]
final details = renderer.getNearestDatumDetailPerSeries(
new Point<double>(70.0 + 10.0, 20.0 + 100.0 - 5.0),
selectNearestByDomain,
null);
// Verify
expect(details.length, equals(1));
final closest = details[0];
expect(closest.domain, equals(1000));
expect(closest.series.id, equals('bar'));
expect(closest.datum, equals(seriesList[1].data[0]));
expect(closest.domainDistance, equals(10));
expect(closest.measureDistance, equals(5));
});
test('all series without data is skipped', () {
// Setup
final seriesList = <MutableSeries<int>>[
_makeSeries(id: 'foo')..data.clear(),
_makeSeries(id: 'bar')..data.clear(),
];
renderer.configureSeries(seriesList);
renderer.preprocessSeries(seriesList);
renderer.update(seriesList, false);
renderer.paint(new MockCanvas(), 1.0);
// Act
final details = renderer.getNearestDatumDetailPerSeries(
new Point<double>(70.0 + 10.0, 20.0 + 100.0 - 5.0),
selectNearestByDomain,
null);
// Verify
expect(details.length, equals(0));
});
test('single overlay series is skipped', () {
// Setup
final seriesList = <MutableSeries<int>>[
_makeSeries(id: 'foo')..overlaySeries = true,
_makeSeries(id: 'bar'),
];
renderer.configureSeries(seriesList);
renderer.preprocessSeries(seriesList);
renderer.update(seriesList, false);
renderer.paint(new MockCanvas(), 1.0);
// Act
final details = renderer.getNearestDatumDetailPerSeries(
new Point<double>(70.0 + 10.0, 20.0 + 100.0 - 5.0),
selectNearestByDomain,
null);
// Verify
expect(details.length, equals(1));
final closest = details[0];
expect(closest.domain, equals(1000));
expect(closest.series.id, equals('bar'));
expect(closest.datum, equals(seriesList[1].data[0]));
expect(closest.domainDistance, equals(10));
expect(closest.measureDistance, equals(5));
});
test('all overlay series is skipped', () {
// Setup
final seriesList = <MutableSeries<int>>[
_makeSeries(id: 'foo')..overlaySeries = true,
_makeSeries(id: 'bar')..overlaySeries = true,
];
renderer.configureSeries(seriesList);
renderer.preprocessSeries(seriesList);
renderer.update(seriesList, false);
renderer.paint(new MockCanvas(), 1.0);
// Act
final details = renderer.getNearestDatumDetailPerSeries(
new Point<double>(70.0 + 10.0, 20.0 + 100.0 - 5.0),
selectNearestByDomain,
null);
// Verify
expect(details.length, equals(0));
});
});
/////////////////////////////////////////
// Vertical BarRenderer
/////////////////////////////////////////
group('LineRenderer', () {
test('hit test works', () {
// Setup
final seriesList = <MutableSeries<int>>[_makeSeries(id: 'foo')];
renderer.configureSeries(seriesList);
renderer.preprocessSeries(seriesList);
renderer.update(seriesList, false);
renderer.paint(new MockCanvas(), 1.0);
// Act
final details = renderer.getNearestDatumDetailPerSeries(
new Point<double>(70.0 + 10.0, 20.0 + 100.0 - 5.0),
selectNearestByDomain,
null);
// Verify
expect(details.length, equals(1));
final closest = details[0];
expect(closest.domain, equals(1000));
expect(closest.series, equals(seriesList[0]));
expect(closest.datum, equals(seriesList[0].data[0]));
expect(closest.domainDistance, equals(10));
expect(closest.measureDistance, equals(5));
});
test('hit test expands to multiple series', () {
// Setup bar series is 20 measure higher than foo.
final seriesList = <MutableSeries<int>>[
_makeSeries(id: 'foo'),
_makeSeries(id: 'bar', measureOffset: 20),
];
renderer.configureSeries(seriesList);
renderer.preprocessSeries(seriesList);
renderer.update(seriesList, false);
renderer.paint(new MockCanvas(), 1.0);
// Act
final details = renderer.getNearestDatumDetailPerSeries(
new Point<double>(70.0 + 10.0, 20.0 + 100.0 - 5.0),
selectNearestByDomain,
null);
// Verify
expect(details.length, equals(2));
final closest = details[0];
expect(closest.domain, equals(1000));
expect(closest.series.id, equals('foo'));
expect(closest.datum, equals(seriesList[0].data[0]));
expect(closest.domainDistance, equals(10));
expect(closest.measureDistance, equals(5));
final next = details[1];
expect(next.domain, equals(1000));
expect(next.series.id, equals('bar'));
expect(next.datum, equals(seriesList[1].data[0]));
expect(next.domainDistance, equals(10));
expect(next.measureDistance, equals(25)); // 20offset + 10measure - 5pt
});
test('hit test expands with missing data in series', () {
// Setup bar series is 20 measure higher than foo and is missing the
// middle point.
final seriesList = <MutableSeries<int>>[
_makeSeries(id: 'foo'),
_makeSeries(id: 'bar', measureOffset: 20)..data.removeAt(1),
];
renderer.configureSeries(seriesList);
renderer.preprocessSeries(seriesList);
renderer.update(seriesList, false);
renderer.paint(new MockCanvas(), 1.0);
// Act
final details = renderer.getNearestDatumDetailPerSeries(
new Point<double>(70.0 + 100.0 + 10.0, 20.0 + 100.0 - 5.0),
selectNearestByDomain,
null);
// Verify
expect(details.length, equals(2));
final closest = details[0];
expect(closest.domain, equals(2000));
expect(closest.series.id, equals('foo'));
expect(closest.datum, equals(seriesList[0].data[1]));
expect(closest.domainDistance, equals(10));
expect(closest.measureDistance, equals(15));
// bar series jumps to last point since it is missing middle.
final next = details[1];
expect(next.domain, equals(3000));
expect(next.series.id, equals('bar'));
expect(next.datum, equals(seriesList[1].data[1]));
expect(next.domainDistance, equals(90));
expect(next.measureDistance, equals(45.0));
});
test('hit test works for points above drawArea', () {
// Setup
final seriesList = <MutableSeries<int>>[
_makeSeries(id: 'foo')..data[1].clickCount = 500
];
renderer.configureSeries(seriesList);
renderer.preprocessSeries(seriesList);
renderer.update(seriesList, false);
renderer.paint(new MockCanvas(), 1.0);
// Act
final details = renderer.getNearestDatumDetailPerSeries(
new Point<double>(70.0 + 100.0 + 10.0, 20.0 + 10.0),
selectNearestByDomain,
null);
// Verify
expect(details.length, equals(1));
final closest = details[0];
expect(closest.domain, equals(2000));
expect(closest.series, equals(seriesList[0]));
expect(closest.datum, equals(seriesList[0].data[1]));
expect(closest.domainDistance, equals(10));
expect(closest.measureDistance, equals(410)); // 500 - 100 + 10
});
test('no selection for points outside of viewport', () {
// Setup
final seriesList = <MutableSeries<int>>[
_makeSeries(id: 'foo')..data.add(new MyRow(-1000, 20))
];
renderer.configureSeries(seriesList);
renderer.preprocessSeries(seriesList);
renderer.update(seriesList, false);
renderer.paint(new MockCanvas(), 1.0);
// Act
// Note: point is in the axis, over a bar outside of the viewport.
final details = renderer.getNearestDatumDetailPerSeries(
new Point<double>(-0.0, 20.0 + 100.0 - 5.0),
selectNearestByDomain,
null);
// Verify
expect(details.length, equals(0));
});
});
}