diff --git a/test-runtime/interval.test.ts b/test-runtime/interval.test.ts
index f16ff1a48c4..1ece7a2dd2a 100644
--- a/test-runtime/interval.test.ts
+++ b/test-runtime/interval.test.ts
@@ -1,6 +1,6 @@
import {TopLevelSpec} from '../src';
import {SelectionType} from '../src/selection';
-import {brush, embedFn, hits as hitsMaster, spec, testRenderFn, tuples} from './util';
+import {brush, embedFn, geoSpec, hits as hitsMaster, spec, testRenderFn, tuples} from './util';
import {Page} from 'puppeteer/lib/cjs/puppeteer/common/Page';
describe('interval selections at runtime in unit views', () => {
@@ -197,4 +197,26 @@ describe('interval selections at runtime in unit views', () => {
await testRender(`logpow_${i}`);
}
});
+
+ describe('geo-intervals', () => {
+ it('should add IDs to the store', async () => {
+ await embed(geoSpec());
+ const store = await page.evaluate(brush('drag', 1));
+ expect(store).toHaveLength(13);
+ for (const t of store) {
+ expect(t).toHaveProperty('_vgsid_');
+ }
+ await testRender(`geo_1`);
+ });
+
+ it('should respect projections', async () => {
+ await embed(geoSpec({encodings: ['longitude']}));
+ const store = await page.evaluate(brush('drag', 0));
+ expect(store).toHaveLength(20);
+ for (const t of store) {
+ expect(t).toHaveProperty('_vgsid_');
+ }
+ await testRender(`geo_0`);
+ });
+ });
});
diff --git a/test-runtime/resources/interval/translate/geo-0.svg b/test-runtime/resources/interval/translate/geo-0.svg
new file mode 100644
index 00000000000..4dfad2e2813
--- /dev/null
+++ b/test-runtime/resources/interval/translate/geo-0.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/test-runtime/resources/interval/translate/geo-1.svg b/test-runtime/resources/interval/translate/geo-1.svg
new file mode 100644
index 00000000000..f49911ea46d
--- /dev/null
+++ b/test-runtime/resources/interval/translate/geo-1.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/test-runtime/resources/interval/translate/geo-2.svg b/test-runtime/resources/interval/translate/geo-2.svg
new file mode 100644
index 00000000000..232ee66ff79
--- /dev/null
+++ b/test-runtime/resources/interval/translate/geo-2.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/test-runtime/resources/interval/unit/geo_0.svg b/test-runtime/resources/interval/unit/geo_0.svg
new file mode 100644
index 00000000000..bce629d1275
--- /dev/null
+++ b/test-runtime/resources/interval/unit/geo_0.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/test-runtime/resources/interval/unit/geo_1.svg b/test-runtime/resources/interval/unit/geo_1.svg
new file mode 100644
index 00000000000..4dfad2e2813
--- /dev/null
+++ b/test-runtime/resources/interval/unit/geo_1.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/test-runtime/resources/interval/zoom/geo-0.svg b/test-runtime/resources/interval/zoom/geo-0.svg
new file mode 100644
index 00000000000..4dfad2e2813
--- /dev/null
+++ b/test-runtime/resources/interval/zoom/geo-0.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/test-runtime/resources/interval/zoom/geo-1.svg b/test-runtime/resources/interval/zoom/geo-1.svg
new file mode 100644
index 00000000000..1b8107eeb1c
--- /dev/null
+++ b/test-runtime/resources/interval/zoom/geo-1.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/test-runtime/resources/interval/zoom/geo-2.svg b/test-runtime/resources/interval/zoom/geo-2.svg
new file mode 100644
index 00000000000..253b2a67fc9
--- /dev/null
+++ b/test-runtime/resources/interval/zoom/geo-2.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/test-runtime/translate.test.ts b/test-runtime/translate.test.ts
index 2f944c8fd31..ba0c7f3cd9b 100644
--- a/test-runtime/translate.test.ts
+++ b/test-runtime/translate.test.ts
@@ -6,6 +6,7 @@ import {
brush,
compositeTypes,
embedFn,
+ geoSpec,
hits as hitsMaster,
parentSelector,
spec,
@@ -16,117 +17,97 @@ import {
import {Page} from 'puppeteer/lib/cjs/puppeteer/common/Page';
import {TopLevelSpec} from '../src';
-for (const bind of [bound, unbound]) {
- describe(`Translate ${bind} interval selections at runtime`, () => {
- let page: Page;
- let embed: (specification: TopLevelSpec) => Promise;
- let testRender: (filename: string) => Promise;
-
- beforeAll(async () => {
- page = await (global as any).__BROWSER__.newPage();
- embed = embedFn(page);
- testRender = testRenderFn(page, `interval/translate/${bind}`);
- await page.goto('http://0.0.0.0:8000/test-runtime/');
- });
+describe('Translate interval selections at runtime', () => {
+ let page: Page;
+ let embed: (specification: TopLevelSpec) => Promise;
+ let testRender: (filename: string) => Promise;
- afterAll(async () => {
- await page.close();
- });
+ beforeAll(async () => {
+ page = await (global as any).__BROWSER__.newPage();
+ embed = embedFn(page);
+ await page.goto('http://0.0.0.0:8000/test-runtime/');
+ });
- const type = 'interval';
- const hits = hitsMaster.interval;
- const binding = bind === bound ? {bind: 'scales'} : {};
-
- const assertExtent = {
- [unbound]: {
- x: ['isAbove', 'isBelow'],
- y: ['isBelow', 'isAbove']
- },
- [bound]: {
- x: ['isBelow', 'isAbove'],
- y: ['isAbove', 'isBelow']
- }
- };
-
- it('should move back-and-forth', async () => {
- for (let i = 0; i < hits.translate.length; i++) {
- await embed(spec('unit', i, {type, ...binding}));
- const drag = (await page.evaluate(brush('drag', i)))[0];
- await testRender(`${i}-0`);
- const translate = (await page.evaluate(brush('translate', i, null, bind === unbound)))[0];
- assert[assertExtent[bind].x[i]](translate.values[0][0], drag.values[0][0]);
- assert[assertExtent[bind].x[i]](translate.values[0][1], drag.values[0][1]);
- assert[assertExtent[bind].y[i]](translate.values[1][0], drag.values[1][0]);
- assert[assertExtent[bind].y[i]](translate.values[1][1], drag.values[1][1]);
- await testRender(`${i}-1`);
- }
- });
+ afterAll(async () => {
+ await page.close();
+ });
- it('should work with binned domains', async () => {
- for (let i = 0; i < hits.bins.length; i++) {
- await embed(
- spec(
- 'unit',
- 1,
- {type, ...binding, encodings: ['y']},
- {
- x: {aggregate: 'count', type: 'quantitative'},
- y: {bin: true},
- color: {value: 'steelblue', field: null, type: null}
- }
- )
- );
- const drag = (await page.evaluate(brush('bins', i)))[0];
- await testRender(`bins_${i}-0`);
- const translate = (await page.evaluate(brush('bins_translate', i, null, bind === unbound)))[0];
- assert[assertExtent[bind].y[i]](translate.values[0][0], drag.values[0][0]);
- assert[assertExtent[bind].y[i]](translate.values[0][1], drag.values[0][1]);
- await testRender(`bins_${i}-1`);
- }
- });
+ const hits = hitsMaster.interval;
- it('should work with temporal domains', async () => {
- // await jestPuppeteer.debug();
- const values = tuples.map(d => ({...d, a: new Date(2017, d.a)}));
- const toNumber = (a: any) => a[0].values[0].map((d: any) => +d);
-
- for (let i = 0; i < hits.translate.length; i++) {
- await embed(spec('unit', i, {type, ...binding, encodings: ['x']}, {values, x: {type: 'temporal'}}));
- const drag = toNumber(await page.evaluate(brush('drag', i)));
- await testRender(`temporal_${i}-0`);
- const translate = toNumber(await page.evaluate(brush('translate', i, null, bind === unbound)));
- assert[assertExtent[bind].x[i]](translate[0], drag[0]);
- assert[assertExtent[bind].x[i]](translate[1], drag[1]);
- await testRender(`temporal_${i}-1`);
- }
- });
+ for (const bind of [bound, unbound]) {
+ describe(`${bind} intervals`, () => {
+ beforeAll(() => {
+ testRender = testRenderFn(page, `interval/translate/${bind}`);
+ });
- it('should work with log/pow scales', async () => {
- for (let i = 0; i < hits.translate.length; i++) {
- await embed(
- spec(
- 'unit',
- i,
- {type, ...binding},
- {
- x: {scale: {type: 'pow', exponent: 1.5}},
- y: {scale: {type: 'log'}}
- }
- )
- );
- const drag = (await page.evaluate(brush('drag', i)))[0];
- await testRender(`logpow_${i}-0`);
- const translate = (await page.evaluate(brush('translate', i, null, bind === unbound)))[0];
- assert[assertExtent[bind].x[i]](translate.values[0][0], drag.values[0][0]);
- assert[assertExtent[bind].x[i]](translate.values[0][1], drag.values[0][1]);
- assert[assertExtent[bind].y[i]](translate.values[1][0], drag.values[1][0]);
- assert[assertExtent[bind].y[i]](translate.values[1][1], drag.values[1][1]);
- await testRender(`logpow_${i}-1`);
- }
- });
+ const type = 'interval';
+ const binding = bind === bound ? {bind: 'scales'} : {};
- if (bind === unbound) {
- it('should work with ordinal/nominal domains', async () => {
+ const assertExtent = {
+ [unbound]: {
+ x: ['isAbove', 'isBelow'],
+ y: ['isBelow', 'isAbove']
+ },
+ [bound]: {
+ x: ['isBelow', 'isAbove'],
+ y: ['isAbove', 'isBelow']
+ }
+ };
+
+ it('should move back-and-forth', async () => {
+ for (let i = 0; i < hits.translate.length; i++) {
+ await embed(spec('unit', i, {type, ...binding}));
+ const drag = (await page.evaluate(brush('drag', i)))[0];
+ await testRender(`${i}-0`);
+ const translate = (await page.evaluate(brush('translate', i, null, bind === unbound)))[0];
+ assert[assertExtent[bind].x[i]](translate.values[0][0], drag.values[0][0]);
+ assert[assertExtent[bind].x[i]](translate.values[0][1], drag.values[0][1]);
+ assert[assertExtent[bind].y[i]](translate.values[1][0], drag.values[1][0]);
+ assert[assertExtent[bind].y[i]](translate.values[1][1], drag.values[1][1]);
+ await testRender(`${i}-1`);
+ }
+ });
+
+ it('should work with binned domains', async () => {
+ for (let i = 0; i < hits.bins.length; i++) {
+ await embed(
+ spec(
+ 'unit',
+ 1,
+ {type, ...binding, encodings: ['y']},
+ {
+ x: {aggregate: 'count', type: 'quantitative'},
+ y: {bin: true},
+ color: {value: 'steelblue', field: null, type: null}
+ }
+ )
+ );
+ const drag = (await page.evaluate(brush('bins', i)))[0];
+ await testRender(`bins_${i}-0`);
+ const translate = (await page.evaluate(brush('bins_translate', i, null, bind === unbound)))[0];
+ assert[assertExtent[bind].y[i]](translate.values[0][0], drag.values[0][0]);
+ assert[assertExtent[bind].y[i]](translate.values[0][1], drag.values[0][1]);
+ await testRender(`bins_${i}-1`);
+ }
+ });
+
+ it('should work with temporal domains', async () => {
+ // await jestPuppeteer.debug();
+ const values = tuples.map(d => ({...d, a: new Date(2017, d.a)}));
+ const toNumber = (a: any) => a[0].values[0].map((d: any) => +d);
+
+ for (let i = 0; i < hits.translate.length; i++) {
+ await embed(spec('unit', i, {type, ...binding, encodings: ['x']}, {values, x: {type: 'temporal'}}));
+ const drag = toNumber(await page.evaluate(brush('drag', i)));
+ await testRender(`temporal_${i}-0`);
+ const translate = toNumber(await page.evaluate(brush('translate', i, null, bind === unbound)));
+ assert[assertExtent[bind].x[i]](translate[0], drag[0]);
+ assert[assertExtent[bind].x[i]](translate[1], drag[1]);
+ await testRender(`temporal_${i}-1`);
+ }
+ });
+
+ it('should work with log/pow scales', async () => {
for (let i = 0; i < hits.translate.length; i++) {
await embed(
spec(
@@ -134,48 +115,89 @@ for (const bind of [bound, unbound]) {
i,
{type, ...binding},
{
- x: {type: 'ordinal'},
- y: {type: 'nominal'}
+ x: {scale: {type: 'pow', exponent: 1.5}},
+ y: {scale: {type: 'log'}}
}
)
);
const drag = (await page.evaluate(brush('drag', i)))[0];
- await testRender(`ord_${i}-0`);
- const translate = (await page.evaluate(brush('translate', i, null, true)))[0];
+ await testRender(`logpow_${i}-0`);
+ const translate = (await page.evaluate(brush('translate', i, null, bind === unbound)))[0];
assert[assertExtent[bind].x[i]](translate.values[0][0], drag.values[0][0]);
assert[assertExtent[bind].x[i]](translate.values[0][1], drag.values[0][1]);
assert[assertExtent[bind].y[i]](translate.values[1][0], drag.values[1][0]);
assert[assertExtent[bind].y[i]](translate.values[1][1], drag.values[1][1]);
- await testRender(`ord_${i}-1`);
+ await testRender(`logpow_${i}-1`);
}
});
- } else {
- for (const specType of compositeTypes) {
- const assertExtents = {
- repeat: {
- x: ['isBelow', 'isBelow', 'isBelow'],
- y: ['isAbove', 'isAbove', 'isAbove']
- },
- facet: {
- x: ['isBelow', 'isBelow', 'isBelow'],
- y: ['isBelow', 'isAbove', 'isBelow']
- }
- };
- it(`should work with shared scales in ${specType} views`, async () => {
- for (let i = 0; i < hits[specType].length; i++) {
- await embed(spec(specType, 0, {type, ...binding}, {resolve: {scale: {x: 'shared', y: 'shared'}}}));
- const parent = parentSelector(specType, i);
- const xscale = await page.evaluate('view._runtime.scales.x.value.domain()');
- const yscale = await page.evaluate('view._runtime.scales.y.value.domain()');
- const drag = (await page.evaluate(brush(specType, i, parent)))[0];
- assert[assertExtents[specType].x[i]](drag.values[0][0], xscale[0], `iter: ${i}`);
- assert[assertExtents[specType].x[i]](drag.values[0][1], xscale[1], `iter: ${i}`);
- assert[assertExtents[specType].y[i]](drag.values[1][0], yscale[0], `iter: ${i}`);
- assert[assertExtents[specType].y[i]](drag.values[1][1], yscale[1], `iter: ${i}`);
- await testRender(`${specType}_${i}`);
+
+ if (bind === unbound) {
+ it('should work with ordinal/nominal domains', async () => {
+ for (let i = 0; i < hits.translate.length; i++) {
+ await embed(
+ spec(
+ 'unit',
+ i,
+ {type, ...binding},
+ {
+ x: {type: 'ordinal'},
+ y: {type: 'nominal'}
+ }
+ )
+ );
+ const drag = (await page.evaluate(brush('drag', i)))[0];
+ await testRender(`ord_${i}-0`);
+ const translate = (await page.evaluate(brush('translate', i, null, true)))[0];
+ assert[assertExtent[bind].x[i]](translate.values[0][0], drag.values[0][0]);
+ assert[assertExtent[bind].x[i]](translate.values[0][1], drag.values[0][1]);
+ assert[assertExtent[bind].y[i]](translate.values[1][0], drag.values[1][0]);
+ assert[assertExtent[bind].y[i]](translate.values[1][1], drag.values[1][1]);
+ await testRender(`ord_${i}-1`);
}
});
+ } else {
+ for (const specType of compositeTypes) {
+ const assertExtents = {
+ repeat: {
+ x: ['isBelow', 'isBelow', 'isBelow'],
+ y: ['isAbove', 'isAbove', 'isAbove']
+ },
+ facet: {
+ x: ['isBelow', 'isBelow', 'isBelow'],
+ y: ['isBelow', 'isAbove', 'isBelow']
+ }
+ };
+ it(`should work with shared scales in ${specType} views`, async () => {
+ for (let i = 0; i < hits[specType].length; i++) {
+ await embed(spec(specType, 0, {type, ...binding}, {resolve: {scale: {x: 'shared', y: 'shared'}}}));
+ const parent = parentSelector(specType, i);
+ const xscale = await page.evaluate('view._runtime.scales.x.value.domain()');
+ const yscale = await page.evaluate('view._runtime.scales.y.value.domain()');
+ const drag = (await page.evaluate(brush(specType, i, parent)))[0];
+ assert[assertExtents[specType].x[i]](drag.values[0][0], xscale[0], `iter: ${i}`);
+ assert[assertExtents[specType].x[i]](drag.values[0][1], xscale[1], `iter: ${i}`);
+ assert[assertExtents[specType].y[i]](drag.values[1][0], yscale[0], `iter: ${i}`);
+ assert[assertExtents[specType].y[i]](drag.values[1][1], yscale[1], `iter: ${i}`);
+ await testRender(`${specType}_${i}`);
+ }
+ });
+ }
}
+ });
+ }
+
+ it('should work with geo intervals', async () => {
+ testRender = testRenderFn(page, `interval/translate`);
+
+ await embed(geoSpec());
+ const drag = await page.evaluate(brush('drag', 1));
+ expect(drag).toHaveLength(13);
+ await testRender(`geo-0`);
+
+ for (let i = 0; i < hits.translate.length; i++) {
+ const translate = await page.evaluate(brush('translate', i, null, true));
+ expect(translate.length).toBeGreaterThan(0);
+ await testRender(`geo-${i + 1}`);
}
});
-}
+});
diff --git a/test-runtime/util.ts b/test-runtime/util.ts
index 192974f3a59..0928ddbd026 100644
--- a/test-runtime/util.ts
+++ b/test-runtime/util.ts
@@ -3,7 +3,7 @@ import {sync as mkdirp} from 'mkdirp';
import {Page} from 'puppeteer/lib/cjs/puppeteer/common/Page';
import {promisify} from 'util';
import {stringValue} from 'vega-util';
-import {SelectionResolution, SelectionType} from '../src/selection';
+import {IntervalSelectionConfigWithoutType, SelectionResolution, SelectionType} from '../src/selection';
import {NormalizedLayerSpec, NormalizedUnitSpec, TopLevelSpec} from '../src/spec';
const generate = process.env.VL_GENERATE_TESTS;
@@ -189,6 +189,64 @@ export function spec(compose: ComposeType, iter: number, sel: any, opts: any = {
}
}
+export function geoSpec(selDef?: IntervalSelectionConfigWithoutType): TopLevelSpec {
+ return {
+ width: 500,
+ height: 300,
+ projection: {type: 'albersUsa'},
+ data: {
+ values: [
+ {latitude: 31.95376472, longitude: -89.23450472},
+ {latitude: 30.68586111, longitude: -95.01792778},
+ {latitude: 38.94574889, longitude: -104.5698933},
+ {latitude: 42.74134667, longitude: -78.05208056},
+ {latitude: 30.6880125, longitude: -81.90594389},
+ {latitude: 34.49166667, longitude: -88.20111111},
+ {latitude: 32.85048667, longitude: -86.61145333},
+ {latitude: 43.08751, longitude: -88.17786917},
+ {latitude: 40.67331278, longitude: -80.64140639},
+ {latitude: 40.44725889, longitude: -92.22696056},
+ {latitude: 33.93011222, longitude: -89.34285194},
+ {latitude: 46.88384889, longitude: -96.35089861},
+ {latitude: 41.51961917, longitude: -87.40109333},
+ {latitude: 31.42127556, longitude: -97.79696778},
+ {latitude: 39.60416667, longitude: -116.0050597},
+ {latitude: 32.46047167, longitude: -85.68003611},
+ {latitude: 41.98934083, longitude: -88.10124278},
+ {latitude: 48.88434111, longitude: -99.62087694},
+ {latitude: 33.53456583, longitude: -89.31256917},
+ {latitude: 41.43156583, longitude: -74.39191722},
+ {latitude: 41.97602222, longitude: -114.6580911},
+ {latitude: 41.30716667, longitude: -85.06433333},
+ {latitude: 32.52883861, longitude: -94.97174556},
+ {latitude: 42.57450861, longitude: -84.81143139},
+ {latitude: 41.11668056, longitude: -98.05033639},
+ {latitude: 32.52943944, longitude: -86.32822139},
+ {latitude: 48.30079861, longitude: -102.4063514},
+ {latitude: 40.65138528, longitude: -98.07978667},
+ {latitude: 32.76124611, longitude: -89.53007139},
+ {latitude: 32.11931306, longitude: -88.1274625}
+ ]
+ },
+ mark: 'circle',
+ params: [
+ {
+ name: 'sel',
+ select: {type: 'interval', ...selDef}
+ }
+ ],
+ encoding: {
+ longitude: {field: 'longitude', type: 'quantitative'},
+ latitude: {field: 'latitude', type: 'quantitative'},
+ color: {
+ condition: {param: 'sel', empty: false, value: 'goldenrod'},
+ value: 'steelblue'
+ },
+ size: {value: 10}
+ }
+ };
+}
+
export function unitNameRegex(specType: ComposeType, idx: number) {
const name = UNIT_NAMES[specType][idx].replace('child_', '');
return new RegExp(`child(.*?)_${name}`);
diff --git a/test-runtime/zoom.test.ts b/test-runtime/zoom.test.ts
index 4ae6169ef44..f823ec3ba01 100644
--- a/test-runtime/zoom.test.ts
+++ b/test-runtime/zoom.test.ts
@@ -1,7 +1,18 @@
/* eslint-disable jest/expect-expect */
import {assert} from 'chai';
-import {bound, brush, compositeTypes, embedFn, parentSelector, spec, testRenderFn, tuples, unbound} from './util';
+import {
+ bound,
+ brush,
+ compositeTypes,
+ embedFn,
+ geoSpec,
+ parentSelector,
+ spec,
+ testRenderFn,
+ tuples,
+ unbound
+} from './util';
const hits = {
zoom: [9, 23],
bins: [8, 2]
@@ -16,140 +27,115 @@ function zoom(key: string, idx: number, direction: InOut, parent?: string, targe
return `zoom(${hits[key][idx]}, ${delta}, ${parent}, ${targetBrush})`;
}
-const cmp = (a: number, b: number) => a - b;
+describe('Zoom interval selections at runtime', () => {
+ let page: Page;
+ let embed: (specification: TopLevelSpec) => Promise;
+ let testRender: (filename: string) => Promise;
-for (const bind of [bound, unbound]) {
- describe(`Zoom ${bind} interval selections at runtime`, () => {
- let page: Page;
- let embed: (specification: TopLevelSpec) => Promise;
- let testRender: (filename: string) => Promise;
-
- beforeAll(async () => {
- page = await (global as any).__BROWSER__.newPage();
- embed = embedFn(page);
- testRender = testRenderFn(page, `interval/zoom/${bind}`);
- await page.goto('http://0.0.0.0:8000/test-runtime/');
- });
-
- afterAll(async () => {
- await page.close();
- });
+ beforeAll(async () => {
+ page = await (global as any).__BROWSER__.newPage();
+ embed = embedFn(page);
+ await page.goto('http://0.0.0.0:8000/test-runtime/');
+ });
- const type = 'interval';
- const binding = bind === bound ? {bind: 'scales'} : {};
+ afterAll(async () => {
+ await page.close();
+ });
- const assertExtent = {
- in: ['isAtLeast', 'isAtMost'],
- out: ['isAtMost', 'isAtLeast']
- };
+ for (const bind of [bound, unbound]) {
+ describe(`Zoom ${bind} interval selections at runtime`, () => {
+ beforeAll(() => {
+ testRender = testRenderFn(page, `interval/zoom/${bind}`);
+ });
- async function setup(brushKey: string, idx: number, encodings: string[], parent?: string) {
- const inOut: InOut = idx % 2 ? 'out' : 'in';
- let xold: number[];
- let yold: number[];
+ const type = 'interval';
+ const binding = bind === bound ? {bind: 'scales'} : {};
+ const cmp = (a: number, b: number) => a - b;
+
+ const assertExtent = {
+ in: ['isAtLeast', 'isAtMost'],
+ out: ['isAtMost', 'isAtLeast']
+ };
+
+ async function setup(brushKey: string, idx: number, encodings: string[], parent?: string) {
+ const inOut: InOut = idx % 2 ? 'out' : 'in';
+ let xold: number[];
+ let yold: number[];
+
+ if (bind === unbound) {
+ const drag = (await page.evaluate(brush(brushKey, idx, parent)))[0];
+ xold = drag.values[0].sort(cmp);
+ yold = encodings.includes('y') ? drag.values[encodings.indexOf('x') + 1].sort(cmp) : null;
+ } else {
+ xold = JSON.parse((await page.evaluate('JSON.stringify(view._runtime.scales.x.value.domain())')) as string);
+ yold = (await page.evaluate('view._runtime.scales.y.value.domain()')) as number[];
+ }
- if (bind === unbound) {
- const drag = (await page.evaluate(brush(brushKey, idx, parent)))[0];
- xold = drag.values[0].sort(cmp);
- yold = encodings.includes('y') ? drag.values[encodings.indexOf('x') + 1].sort(cmp) : null;
- } else {
- xold = JSON.parse((await page.evaluate('JSON.stringify(view._runtime.scales.x.value.domain())')) as string);
- yold = (await page.evaluate('view._runtime.scales.y.value.domain()')) as number[];
+ return {inOut, xold, yold};
}
- return {inOut, xold, yold};
- }
+ it('should zoom in and out', async () => {
+ for (let i = 0; i < hits.zoom.length; i++) {
+ await embed(spec('unit', i, {type, ...binding}));
+ const {inOut, xold, yold} = await setup('drag', i, ['x', 'y']);
+ await testRender(`${inOut}-0`);
- it('should zoom in and out', async () => {
- for (let i = 0; i < hits.zoom.length; i++) {
- await embed(spec('unit', i, {type, ...binding}));
- const {inOut, xold, yold} = await setup('drag', i, ['x', 'y']);
- await testRender(`${inOut}-0`);
-
- const zoomed = (await page.evaluate(zoom('zoom', i, inOut, null, bind === unbound)))[0];
- const xnew = zoomed.values[0].sort(cmp);
- const ynew = zoomed.values[1].sort(cmp);
- await testRender(`${inOut}-1`);
- assert[assertExtent[inOut][0]](xnew[0], xold[0]);
- assert[assertExtent[inOut][1]](xnew[1], xold[1]);
- assert[assertExtent[inOut][0]](ynew[0], yold[0]);
- assert[assertExtent[inOut][1]](ynew[1], yold[1]);
- }
- });
+ const zoomed = (await page.evaluate(zoom('zoom', i, inOut, null, bind === unbound)))[0];
+ const xnew = zoomed.values[0].sort(cmp);
+ const ynew = zoomed.values[1].sort(cmp);
+ await testRender(`${inOut}-1`);
+ assert[assertExtent[inOut][0]](xnew[0], xold[0]);
+ assert[assertExtent[inOut][1]](xnew[1], xold[1]);
+ assert[assertExtent[inOut][0]](ynew[0], yold[0]);
+ assert[assertExtent[inOut][1]](ynew[1], yold[1]);
+ }
+ });
- it('should work with binned domains', async () => {
- for (let i = 0; i < hits.bins.length; i++) {
- const encodings = ['y'];
- await embed(
- spec(
- 'unit',
- 1,
- {type, ...binding, encodings},
- {
- x: {aggregate: 'count', type: 'quantitative'},
- y: {bin: true},
- color: {value: 'steelblue', field: null, type: null}
- }
- )
- );
+ it('should work with binned domains', async () => {
+ for (let i = 0; i < hits.bins.length; i++) {
+ const encodings = ['y'];
+ await embed(
+ spec(
+ 'unit',
+ 1,
+ {type, ...binding, encodings},
+ {
+ x: {aggregate: 'count', type: 'quantitative'},
+ y: {bin: true},
+ color: {value: 'steelblue', field: null, type: null}
+ }
+ )
+ );
- const {inOut, yold} = await setup('bins', i, encodings);
- await testRender(`bins_${inOut}-0`);
+ const {inOut, yold} = await setup('bins', i, encodings);
+ await testRender(`bins_${inOut}-0`);
- const zoomed = (await page.evaluate(zoom('bins', i, inOut, null, bind === unbound)))[0];
- const ynew = zoomed.values[0].sort(cmp);
- assert[assertExtent[inOut][0]](ynew[0], yold[0]);
- assert[assertExtent[inOut][1]](ynew[1], yold[1]);
- await testRender(`bins_${inOut}-1`);
- }
- });
-
- it('should work with temporal domains', async () => {
- const values = tuples.map(d => ({...d, a: new Date(2017, d.a)}));
- const encodings = ['x'];
+ const zoomed = (await page.evaluate(zoom('bins', i, inOut, null, bind === unbound)))[0];
+ const ynew = zoomed.values[0].sort(cmp);
+ assert[assertExtent[inOut][0]](ynew[0], yold[0]);
+ assert[assertExtent[inOut][1]](ynew[1], yold[1]);
+ await testRender(`bins_${inOut}-1`);
+ }
+ });
- for (let i = 0; i < hits.zoom.length; i++) {
- await embed(spec('unit', i, {type, ...binding, encodings}, {values, x: {type: 'temporal'}}));
- const {inOut, xold} = await setup('drag', i, encodings);
- await testRender(`temporal_${inOut}-0`);
+ it('should work with temporal domains', async () => {
+ const values = tuples.map(d => ({...d, a: new Date(2017, d.a)}));
+ const encodings = ['x'];
- const zoomed = (await page.evaluate(zoom('zoom', i, inOut, null, bind === unbound)))[0];
- const xnew = zoomed.values[0].sort(cmp);
- assert[assertExtent[inOut][0]](+xnew[0], +new Date(xold[0]));
- assert[assertExtent[inOut][1]](+xnew[1], +new Date(xold[1]));
- await testRender(`temporal_${inOut}-1`);
- }
- });
+ for (let i = 0; i < hits.zoom.length; i++) {
+ await embed(spec('unit', i, {type, ...binding, encodings}, {values, x: {type: 'temporal'}}));
+ const {inOut, xold} = await setup('drag', i, encodings);
+ await testRender(`temporal_${inOut}-0`);
- it('should work with log/pow scales', async () => {
- for (let i = 0; i < hits.zoom.length; i++) {
- await embed(
- spec(
- 'unit',
- i,
- {type, ...binding},
- {
- x: {scale: {type: 'pow', exponent: 1.5}},
- y: {scale: {type: 'log'}}
- }
- )
- );
- const {inOut, xold, yold} = await setup('drag', i, ['x', 'y']);
- await testRender(`logpow_${inOut}-0`);
-
- const zoomed = (await page.evaluate(zoom('zoom', i, inOut, null, bind === unbound)))[0];
- const xnew = zoomed.values[0].sort(cmp);
- const ynew = zoomed.values[1].sort(cmp);
- assert[assertExtent[inOut][0]](xnew[0], xold[0]);
- assert[assertExtent[inOut][1]](xnew[1], xold[1]);
- assert[assertExtent[inOut][0]](ynew[0], yold[0]);
- assert[assertExtent[inOut][1]](ynew[1], yold[1]);
- await testRender(`logpow_${inOut}-1`);
- }
- });
+ const zoomed = (await page.evaluate(zoom('zoom', i, inOut, null, bind === unbound)))[0];
+ const xnew = zoomed.values[0].sort(cmp);
+ assert[assertExtent[inOut][0]](+xnew[0], +new Date(xold[0]));
+ assert[assertExtent[inOut][1]](+xnew[1], +new Date(xold[1]));
+ await testRender(`temporal_${inOut}-1`);
+ }
+ });
- if (bind === unbound) {
- it('should work with ordinal/nominal domains', async () => {
+ it('should work with log/pow scales', async () => {
for (let i = 0; i < hits.zoom.length; i++) {
await embed(
spec(
@@ -157,47 +143,91 @@ for (const bind of [bound, unbound]) {
i,
{type, ...binding},
{
- x: {type: 'ordinal'},
- y: {type: 'nominal'}
+ x: {scale: {type: 'pow', exponent: 1.5}},
+ y: {scale: {type: 'log'}}
}
)
);
const {inOut, xold, yold} = await setup('drag', i, ['x', 'y']);
- await testRender(`ord_${inOut}-0`);
+ await testRender(`logpow_${inOut}-0`);
const zoomed = (await page.evaluate(zoom('zoom', i, inOut, null, bind === unbound)))[0];
const xnew = zoomed.values[0].sort(cmp);
const ynew = zoomed.values[1].sort(cmp);
-
- if (inOut === 'in') {
- expect(xnew.length).toBeLessThanOrEqual(xold.length);
- expect(ynew.length).toBeLessThanOrEqual(yold.length);
- } else {
- expect(xnew.length).toBeGreaterThanOrEqual(xold.length);
- expect(ynew.length).toBeGreaterThanOrEqual(yold.length);
- }
-
- await testRender(`ord_${inOut}-1`);
+ assert[assertExtent[inOut][0]](xnew[0], xold[0]);
+ assert[assertExtent[inOut][1]](xnew[1], xold[1]);
+ assert[assertExtent[inOut][0]](ynew[0], yold[0]);
+ assert[assertExtent[inOut][1]](ynew[1], yold[1]);
+ await testRender(`logpow_${inOut}-1`);
}
});
- } else {
- for (const specType of compositeTypes) {
- it(`should work with shared scales in ${specType} views`, async () => {
- for (let i = 0; i < hits.bins.length; i++) {
- await embed(spec(specType, 0, {type, ...binding}, {resolve: {scale: {x: 'shared', y: 'shared'}}}));
- const parent = parentSelector(specType, i);
- const {inOut, xold, yold} = await setup(specType, i, ['x', 'y'], parent);
- const zoomed = (await page.evaluate(zoom('bins', i, inOut, null, bind === unbound)))[0];
+
+ if (bind === unbound) {
+ it('should work with ordinal/nominal domains', async () => {
+ for (let i = 0; i < hits.zoom.length; i++) {
+ await embed(
+ spec(
+ 'unit',
+ i,
+ {type, ...binding},
+ {
+ x: {type: 'ordinal'},
+ y: {type: 'nominal'}
+ }
+ )
+ );
+ const {inOut, xold, yold} = await setup('drag', i, ['x', 'y']);
+ await testRender(`ord_${inOut}-0`);
+
+ const zoomed = (await page.evaluate(zoom('zoom', i, inOut, null, bind === unbound)))[0];
const xnew = zoomed.values[0].sort(cmp);
const ynew = zoomed.values[1].sort(cmp);
- assert[assertExtent[inOut][0]](xnew[0], xold[0]);
- assert[assertExtent[inOut][1]](xnew[1], xold[1]);
- assert[assertExtent[inOut][0]](ynew[0], yold[0]);
- assert[assertExtent[inOut][1]](ynew[1], yold[1]);
- await testRender(`${specType}_${inOut}`);
+
+ if (inOut === 'in') {
+ expect(xnew.length).toBeLessThanOrEqual(xold.length);
+ expect(ynew.length).toBeLessThanOrEqual(yold.length);
+ } else {
+ expect(xnew.length).toBeGreaterThanOrEqual(xold.length);
+ expect(ynew.length).toBeGreaterThanOrEqual(yold.length);
+ }
+
+ await testRender(`ord_${inOut}-1`);
}
});
+ } else {
+ for (const specType of compositeTypes) {
+ it(`should work with shared scales in ${specType} views`, async () => {
+ for (let i = 0; i < hits.bins.length; i++) {
+ await embed(spec(specType, 0, {type, ...binding}, {resolve: {scale: {x: 'shared', y: 'shared'}}}));
+ const parent = parentSelector(specType, i);
+ const {inOut, xold, yold} = await setup(specType, i, ['x', 'y'], parent);
+ const zoomed = (await page.evaluate(zoom('bins', i, inOut, null, bind === unbound)))[0];
+ const xnew = zoomed.values[0].sort(cmp);
+ const ynew = zoomed.values[1].sort(cmp);
+ assert[assertExtent[inOut][0]](xnew[0], xold[0]);
+ assert[assertExtent[inOut][1]](xnew[1], xold[1]);
+ assert[assertExtent[inOut][0]](ynew[0], yold[0]);
+ assert[assertExtent[inOut][1]](ynew[1], yold[1]);
+ await testRender(`${specType}_${inOut}`);
+ }
+ });
+ }
}
+ });
+ }
+
+ it('should work with geo intervals', async () => {
+ testRender = testRenderFn(page, `interval/zoom`);
+
+ await embed(geoSpec());
+ const drag = await page.evaluate(brush('drag', 1));
+ expect(drag).toHaveLength(13);
+ await testRender(`geo-0`);
+
+ for (let i = 0; i < hits.zoom.length; i++) {
+ const zoomed = await page.evaluate(zoom('zoom', i, i % 2 ? 'out' : 'in', null, true));
+ expect(zoomed.length).toBeGreaterThan(0);
+ await testRender(`geo-${i + 1}`);
}
});
-}
+});