mirror of
https://github.com/apache/superset.git
synced 2026-04-18 23:55:00 +00:00
chore: Moves spec files to the src folder - iteration 8 (#17899)
* chore: Moves spec files to the src folder - iteration 8 * Fixes tests
This commit is contained in:
committed by
GitHub
parent
aa91662ec8
commit
e6ef7da758
@@ -1,539 +0,0 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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 sinon from 'sinon';
|
||||
|
||||
import { ActionCreators as UndoActionCreators } from 'redux-undo';
|
||||
|
||||
import {
|
||||
UPDATE_COMPONENTS,
|
||||
updateComponents,
|
||||
DELETE_COMPONENT,
|
||||
deleteComponent,
|
||||
CREATE_COMPONENT,
|
||||
CREATE_TOP_LEVEL_TABS,
|
||||
createTopLevelTabs,
|
||||
DELETE_TOP_LEVEL_TABS,
|
||||
deleteTopLevelTabs,
|
||||
resizeComponent,
|
||||
MOVE_COMPONENT,
|
||||
handleComponentDrop,
|
||||
updateDashboardTitle,
|
||||
undoLayoutAction,
|
||||
redoLayoutAction,
|
||||
} from 'src/dashboard/actions/dashboardLayout';
|
||||
|
||||
import { setUnsavedChanges } from 'src/dashboard/actions/dashboardState';
|
||||
import * as dashboardFilters from 'src/dashboard/actions/dashboardFilters';
|
||||
import { ADD_TOAST } from 'src/components/MessageToasts/actions';
|
||||
|
||||
import {
|
||||
DASHBOARD_GRID_TYPE,
|
||||
ROW_TYPE,
|
||||
CHART_TYPE,
|
||||
TABS_TYPE,
|
||||
TAB_TYPE,
|
||||
} from 'src/dashboard/util/componentTypes';
|
||||
|
||||
import {
|
||||
DASHBOARD_HEADER_ID,
|
||||
DASHBOARD_GRID_ID,
|
||||
DASHBOARD_ROOT_ID,
|
||||
NEW_COMPONENTS_SOURCE_ID,
|
||||
NEW_ROW_ID,
|
||||
} from 'src/dashboard/util/constants';
|
||||
|
||||
describe('dashboardLayout actions', () => {
|
||||
const mockState = {
|
||||
dashboardState: {
|
||||
hasUnsavedChanges: true, // don't dispatch setUnsavedChanges() after every action
|
||||
},
|
||||
dashboardInfo: {},
|
||||
dashboardLayout: {
|
||||
past: [],
|
||||
present: {},
|
||||
future: {},
|
||||
},
|
||||
};
|
||||
|
||||
function setup(stateOverrides) {
|
||||
const state = { ...mockState, ...stateOverrides };
|
||||
const getState = sinon.spy(() => state);
|
||||
const dispatch = sinon.spy();
|
||||
|
||||
return { getState, dispatch, state };
|
||||
}
|
||||
beforeEach(() => {
|
||||
sinon.spy(dashboardFilters, 'updateLayoutComponents');
|
||||
});
|
||||
afterEach(() => {
|
||||
dashboardFilters.updateLayoutComponents.restore();
|
||||
});
|
||||
|
||||
describe('updateComponents', () => {
|
||||
it('should dispatch an updateLayout action', () => {
|
||||
const { getState, dispatch } = setup();
|
||||
const nextComponents = { 1: {} };
|
||||
const thunk = updateComponents(nextComponents);
|
||||
thunk(dispatch, getState);
|
||||
expect(dispatch.callCount).toBe(1);
|
||||
expect(dispatch.getCall(0).args[0]).toEqual({
|
||||
type: UPDATE_COMPONENTS,
|
||||
payload: { nextComponents },
|
||||
});
|
||||
|
||||
// update component should not trigger action for dashboardFilters
|
||||
expect(dashboardFilters.updateLayoutComponents.callCount).toEqual(0);
|
||||
});
|
||||
|
||||
it('should dispatch a setUnsavedChanges action if hasUnsavedChanges=false', () => {
|
||||
const { getState, dispatch } = setup({
|
||||
dashboardState: { hasUnsavedChanges: false },
|
||||
});
|
||||
const nextComponents = { 1: {} };
|
||||
const thunk = updateComponents(nextComponents);
|
||||
thunk(dispatch, getState);
|
||||
expect(dispatch.getCall(1).args[0]).toEqual(setUnsavedChanges(true));
|
||||
|
||||
// update component should not trigger action for dashboardFilters
|
||||
expect(dashboardFilters.updateLayoutComponents.callCount).toEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('deleteComponents', () => {
|
||||
it('should dispatch an deleteComponent action', () => {
|
||||
const { getState, dispatch } = setup();
|
||||
const thunk = deleteComponent('id', 'parentId');
|
||||
thunk(dispatch, getState);
|
||||
expect(dispatch.getCall(0).args[0]).toEqual({
|
||||
type: DELETE_COMPONENT,
|
||||
payload: { id: 'id', parentId: 'parentId' },
|
||||
});
|
||||
|
||||
// delete components should trigger action for dashboardFilters
|
||||
expect(dashboardFilters.updateLayoutComponents.callCount).toEqual(1);
|
||||
});
|
||||
|
||||
it('should dispatch a setUnsavedChanges action if hasUnsavedChanges=false', () => {
|
||||
const { getState, dispatch } = setup({
|
||||
dashboardState: { hasUnsavedChanges: false },
|
||||
});
|
||||
const thunk = deleteComponent('id', 'parentId');
|
||||
thunk(dispatch, getState);
|
||||
expect(dispatch.getCall(2).args[0]).toEqual(setUnsavedChanges(true));
|
||||
|
||||
// delete components should trigger action for dashboardFilters
|
||||
expect(dashboardFilters.updateLayoutComponents.callCount).toEqual(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateDashboardTitle', () => {
|
||||
it('should dispatch an updateComponent action for the header component', () => {
|
||||
const { getState, dispatch } = setup();
|
||||
const thunk1 = updateDashboardTitle('new text');
|
||||
thunk1(dispatch, getState);
|
||||
|
||||
const thunk2 = dispatch.getCall(0).args[0];
|
||||
thunk2(dispatch, getState);
|
||||
|
||||
expect(dispatch.getCall(1).args[0]).toEqual({
|
||||
type: UPDATE_COMPONENTS,
|
||||
payload: {
|
||||
nextComponents: {
|
||||
[DASHBOARD_HEADER_ID]: {
|
||||
meta: { text: 'new text' },
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// update dashboard title should not trigger action for dashboardFilters
|
||||
expect(dashboardFilters.updateLayoutComponents.callCount).toEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('createTopLevelTabs', () => {
|
||||
it('should dispatch a createTopLevelTabs action', () => {
|
||||
const { getState, dispatch } = setup();
|
||||
const dropResult = {};
|
||||
const thunk = createTopLevelTabs(dropResult);
|
||||
thunk(dispatch, getState);
|
||||
expect(dispatch.getCall(0).args[0]).toEqual({
|
||||
type: CREATE_TOP_LEVEL_TABS,
|
||||
payload: { dropResult },
|
||||
});
|
||||
|
||||
// create top level tabs should trigger action for dashboardFilters
|
||||
expect(dashboardFilters.updateLayoutComponents.callCount).toEqual(1);
|
||||
});
|
||||
|
||||
it('should dispatch a setUnsavedChanges action if hasUnsavedChanges=false', () => {
|
||||
const { getState, dispatch } = setup({
|
||||
dashboardState: { hasUnsavedChanges: false },
|
||||
});
|
||||
const dropResult = {};
|
||||
const thunk = createTopLevelTabs(dropResult);
|
||||
thunk(dispatch, getState);
|
||||
expect(dispatch.getCall(2).args[0]).toEqual(setUnsavedChanges(true));
|
||||
|
||||
// create top level tabs should trigger action for dashboardFilters
|
||||
expect(dashboardFilters.updateLayoutComponents.callCount).toEqual(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('deleteTopLevelTabs', () => {
|
||||
it('should dispatch a deleteTopLevelTabs action', () => {
|
||||
const { getState, dispatch } = setup();
|
||||
const dropResult = {};
|
||||
const thunk = deleteTopLevelTabs(dropResult);
|
||||
thunk(dispatch, getState);
|
||||
expect(dispatch.getCall(0).args[0]).toEqual({
|
||||
type: DELETE_TOP_LEVEL_TABS,
|
||||
payload: {},
|
||||
});
|
||||
|
||||
// delete top level tabs should trigger action for dashboardFilters
|
||||
expect(dashboardFilters.updateLayoutComponents.callCount).toEqual(1);
|
||||
});
|
||||
|
||||
it('should dispatch a setUnsavedChanges action if hasUnsavedChanges=false', () => {
|
||||
const { getState, dispatch } = setup({
|
||||
dashboardState: { hasUnsavedChanges: false },
|
||||
});
|
||||
const dropResult = {};
|
||||
const thunk = deleteTopLevelTabs(dropResult);
|
||||
thunk(dispatch, getState);
|
||||
expect(dispatch.getCall(2).args[0]).toEqual(setUnsavedChanges(true));
|
||||
|
||||
// delete top level tabs should trigger action for dashboardFilters
|
||||
expect(dashboardFilters.updateLayoutComponents.callCount).toEqual(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('resizeComponent', () => {
|
||||
const dashboardLayout = {
|
||||
...mockState.dashboardLayout,
|
||||
present: {
|
||||
1: {
|
||||
id: 1,
|
||||
children: [],
|
||||
meta: {
|
||||
width: 1,
|
||||
height: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
it('should update the size of the component', () => {
|
||||
const { getState, dispatch } = setup({
|
||||
dashboardLayout,
|
||||
});
|
||||
|
||||
const thunk1 = resizeComponent({ id: 1, width: 10, height: 3 });
|
||||
thunk1(dispatch, getState);
|
||||
|
||||
const thunk2 = dispatch.getCall(0).args[0];
|
||||
thunk2(dispatch, getState);
|
||||
|
||||
expect(dispatch.callCount).toBe(2);
|
||||
expect(dispatch.getCall(1).args[0]).toEqual({
|
||||
type: UPDATE_COMPONENTS,
|
||||
payload: {
|
||||
nextComponents: {
|
||||
1: {
|
||||
id: 1,
|
||||
children: [],
|
||||
meta: {
|
||||
width: 10,
|
||||
height: 3,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(dispatch.callCount).toBe(2);
|
||||
});
|
||||
|
||||
it('should dispatch a setUnsavedChanges action if hasUnsavedChanges=false', () => {
|
||||
const { getState, dispatch } = setup({
|
||||
dashboardState: { hasUnsavedChanges: false },
|
||||
dashboardLayout,
|
||||
});
|
||||
const thunk1 = resizeComponent({ id: 1, width: 10, height: 3 });
|
||||
thunk1(dispatch, getState);
|
||||
|
||||
const thunk2 = dispatch.getCall(0).args[0];
|
||||
thunk2(dispatch, getState);
|
||||
|
||||
expect(dispatch.callCount).toBe(3);
|
||||
|
||||
// resize components should not trigger action for dashboardFilters
|
||||
expect(dashboardFilters.updateLayoutComponents.callCount).toEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('handleComponentDrop', () => {
|
||||
it('should create a component if it is new', () => {
|
||||
const { getState, dispatch } = setup();
|
||||
const dropResult = {
|
||||
source: { id: NEW_COMPONENTS_SOURCE_ID },
|
||||
destination: { id: DASHBOARD_GRID_ID, type: DASHBOARD_GRID_TYPE },
|
||||
dragging: { id: NEW_ROW_ID, type: ROW_TYPE },
|
||||
};
|
||||
|
||||
const handleComponentDropThunk = handleComponentDrop(dropResult);
|
||||
handleComponentDropThunk(dispatch, getState);
|
||||
|
||||
const createComponentThunk = dispatch.getCall(0).args[0];
|
||||
createComponentThunk(dispatch, getState);
|
||||
|
||||
expect(dispatch.getCall(1).args[0]).toEqual({
|
||||
type: CREATE_COMPONENT,
|
||||
payload: {
|
||||
dropResult,
|
||||
},
|
||||
});
|
||||
|
||||
// create components should trigger action for dashboardFilters
|
||||
expect(dashboardFilters.updateLayoutComponents.callCount).toEqual(1);
|
||||
});
|
||||
|
||||
it('should move a component if the component is not new', () => {
|
||||
const { getState, dispatch } = setup({
|
||||
dashboardLayout: {
|
||||
// if 'dragging' is not only child will dispatch deleteComponent thunk
|
||||
present: { id: { type: ROW_TYPE, children: ['_'] } },
|
||||
},
|
||||
});
|
||||
const dropResult = {
|
||||
source: { id: 'id', index: 0, type: ROW_TYPE },
|
||||
destination: { id: DASHBOARD_GRID_ID, type: DASHBOARD_GRID_TYPE },
|
||||
dragging: { id: 'dragging', type: ROW_TYPE },
|
||||
};
|
||||
|
||||
const handleComponentDropThunk = handleComponentDrop(dropResult);
|
||||
handleComponentDropThunk(dispatch, getState);
|
||||
|
||||
const moveComponentThunk = dispatch.getCall(0).args[0];
|
||||
moveComponentThunk(dispatch, getState);
|
||||
|
||||
expect(dispatch.getCall(1).args[0]).toEqual({
|
||||
type: MOVE_COMPONENT,
|
||||
payload: {
|
||||
dropResult,
|
||||
},
|
||||
});
|
||||
|
||||
// create components should trigger action for dashboardFilters
|
||||
expect(dashboardFilters.updateLayoutComponents.callCount).toEqual(1);
|
||||
});
|
||||
|
||||
it('should dispatch a toast if the drop overflows the destination', () => {
|
||||
const { getState, dispatch } = setup({
|
||||
dashboardLayout: {
|
||||
present: {
|
||||
source: { id: 'source', type: ROW_TYPE, children: ['dragging'] },
|
||||
destination: {
|
||||
id: 'destination',
|
||||
type: ROW_TYPE,
|
||||
children: ['rowChild'],
|
||||
},
|
||||
dragging: { id: 'dragging', type: CHART_TYPE, meta: { width: 1 } },
|
||||
rowChild: { id: 'rowChild', type: CHART_TYPE, meta: { width: 12 } },
|
||||
},
|
||||
},
|
||||
});
|
||||
const dropResult = {
|
||||
source: { id: 'source', type: ROW_TYPE },
|
||||
destination: { id: 'destination', type: ROW_TYPE },
|
||||
dragging: { id: 'dragging', type: CHART_TYPE, meta: { width: 1 } },
|
||||
};
|
||||
|
||||
const thunk = handleComponentDrop(dropResult);
|
||||
thunk(dispatch, getState);
|
||||
|
||||
expect(dispatch.getCall(0).args[0].type).toEqual(ADD_TOAST);
|
||||
|
||||
expect(dispatch.callCount).toBe(1);
|
||||
});
|
||||
|
||||
it('should delete a parent Row or Tabs if the moved child was the only child', () => {
|
||||
const { getState, dispatch } = setup({
|
||||
dashboardLayout: {
|
||||
present: {
|
||||
parentId: { id: 'parentId', children: ['tabsId'] },
|
||||
tabsId: { id: 'tabsId', type: TABS_TYPE, children: [] },
|
||||
[DASHBOARD_GRID_ID]: {
|
||||
id: DASHBOARD_GRID_ID,
|
||||
type: DASHBOARD_GRID_TYPE,
|
||||
},
|
||||
tabId: { id: 'tabId', type: TAB_TYPE },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const dropResult = {
|
||||
source: { id: 'tabsId', type: TABS_TYPE },
|
||||
destination: { id: DASHBOARD_GRID_ID, type: DASHBOARD_GRID_TYPE },
|
||||
dragging: { id: 'tabId', type: TAB_TYPE },
|
||||
};
|
||||
|
||||
const moveThunk = handleComponentDrop(dropResult);
|
||||
moveThunk(dispatch, getState);
|
||||
|
||||
// first call is move action which is not a thunk
|
||||
const deleteThunk = dispatch.getCall(1).args[0];
|
||||
deleteThunk(dispatch, getState);
|
||||
|
||||
expect(dispatch.getCall(2).args[0]).toEqual({
|
||||
type: DELETE_COMPONENT,
|
||||
payload: {
|
||||
id: 'tabsId',
|
||||
parentId: 'parentId',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should create top-level tabs if dropped on root', () => {
|
||||
const { getState, dispatch } = setup();
|
||||
const dropResult = {
|
||||
source: { id: NEW_COMPONENTS_SOURCE_ID },
|
||||
destination: { id: DASHBOARD_ROOT_ID },
|
||||
dragging: { id: NEW_ROW_ID, type: ROW_TYPE },
|
||||
};
|
||||
|
||||
const thunk1 = handleComponentDrop(dropResult);
|
||||
thunk1(dispatch, getState);
|
||||
|
||||
const thunk2 = dispatch.getCall(0).args[0];
|
||||
thunk2(dispatch, getState);
|
||||
|
||||
expect(dispatch.getCall(1).args[0]).toEqual({
|
||||
type: CREATE_TOP_LEVEL_TABS,
|
||||
payload: {
|
||||
dropResult,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should dispatch a toast if drop top-level tab into nested tab', () => {
|
||||
const { getState, dispatch } = setup({
|
||||
dashboardLayout: {
|
||||
present: {
|
||||
[DASHBOARD_ROOT_ID]: {
|
||||
children: ['TABS-ROOT_TABS'],
|
||||
id: DASHBOARD_ROOT_ID,
|
||||
type: 'ROOT',
|
||||
},
|
||||
'TABS-ROOT_TABS': {
|
||||
children: ['TAB-iMppmTOQy', 'TAB-rt1y8cQ6K9', 'TAB-X_pnCIwPN'],
|
||||
id: 'TABS-ROOT_TABS',
|
||||
meta: {},
|
||||
parents: ['ROOT_ID'],
|
||||
type: TABS_TYPE,
|
||||
},
|
||||
'TABS-ROW_TABS': {
|
||||
children: [
|
||||
'TAB-dKIDBT03bQ',
|
||||
'TAB-PtxY5bbTe',
|
||||
'TAB-Wc2P-yGMz',
|
||||
'TAB-U-xe_si7i',
|
||||
],
|
||||
id: 'TABS-ROW_TABS',
|
||||
meta: {},
|
||||
parents: ['ROOT_ID', 'TABS-ROOT_TABS', 'TAB-X_pnCIwPN'],
|
||||
type: TABS_TYPE,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const dropResult = {
|
||||
source: {
|
||||
id: 'TABS-ROOT_TABS',
|
||||
index: 1,
|
||||
type: TABS_TYPE,
|
||||
},
|
||||
destination: {
|
||||
id: 'TABS-ROW_TABS',
|
||||
index: 1,
|
||||
type: TABS_TYPE,
|
||||
},
|
||||
dragging: {
|
||||
id: 'TAB-rt1y8cQ6K9',
|
||||
meta: { text: 'New Tab' },
|
||||
type: 'TAB',
|
||||
},
|
||||
};
|
||||
|
||||
handleComponentDrop(dropResult)(dispatch, getState);
|
||||
|
||||
expect(dispatch.getCall(0).args[0].type).toEqual(ADD_TOAST);
|
||||
});
|
||||
});
|
||||
|
||||
describe('undoLayoutAction', () => {
|
||||
it('should dispatch a redux-undo .undo() action', () => {
|
||||
const { getState, dispatch } = setup({
|
||||
dashboardLayout: { past: ['non-empty'] },
|
||||
});
|
||||
const thunk = undoLayoutAction();
|
||||
thunk(dispatch, getState);
|
||||
|
||||
expect(dispatch.callCount).toBe(1);
|
||||
expect(dispatch.getCall(0).args[0]).toEqual(UndoActionCreators.undo());
|
||||
});
|
||||
|
||||
it('should dispatch a setUnsavedChanges(false) action history length is zero', () => {
|
||||
const { getState, dispatch } = setup({
|
||||
dashboardLayout: { past: [] },
|
||||
});
|
||||
const thunk = undoLayoutAction();
|
||||
thunk(dispatch, getState);
|
||||
|
||||
expect(dispatch.callCount).toBe(2);
|
||||
expect(dispatch.getCall(1).args[0]).toEqual(setUnsavedChanges(false));
|
||||
});
|
||||
});
|
||||
|
||||
describe('redoLayoutAction', () => {
|
||||
it('should dispatch a redux-undo .redo() action', () => {
|
||||
const { getState, dispatch } = setup();
|
||||
const thunk = redoLayoutAction();
|
||||
thunk(dispatch, getState);
|
||||
|
||||
expect(dispatch.getCall(0).args[0]).toEqual(UndoActionCreators.redo());
|
||||
|
||||
// redo/undo should trigger action for dashboardFilters
|
||||
expect(dashboardFilters.updateLayoutComponents.callCount).toEqual(1);
|
||||
});
|
||||
|
||||
it('should dispatch a setUnsavedChanges(true) action if hasUnsavedChanges=false', () => {
|
||||
const { getState, dispatch } = setup({
|
||||
dashboardState: { hasUnsavedChanges: false },
|
||||
});
|
||||
const thunk = redoLayoutAction();
|
||||
thunk(dispatch, getState);
|
||||
expect(dispatch.getCall(2).args[0]).toEqual(setUnsavedChanges(true));
|
||||
|
||||
// redo/undo should trigger action for dashboardFilters
|
||||
expect(dashboardFilters.updateLayoutComponents.callCount).toEqual(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,121 +0,0 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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 sinon from 'sinon';
|
||||
import { SupersetClient } from '@superset-ui/core';
|
||||
|
||||
import {
|
||||
removeSliceFromDashboard,
|
||||
saveDashboardRequest,
|
||||
} from 'src/dashboard/actions/dashboardState';
|
||||
import { REMOVE_FILTER } from 'src/dashboard/actions/dashboardFilters';
|
||||
import { UPDATE_COMPONENTS_PARENTS_LIST } from 'src/dashboard/actions/dashboardLayout';
|
||||
import { DASHBOARD_GRID_ID } from 'src/dashboard/util/constants';
|
||||
import {
|
||||
filterId,
|
||||
sliceEntitiesForDashboard as sliceEntities,
|
||||
} from 'spec/fixtures/mockSliceEntities';
|
||||
import { emptyFilters } from 'spec/fixtures/mockDashboardFilters';
|
||||
import mockDashboardData from 'spec/fixtures/mockDashboardData';
|
||||
|
||||
describe('dashboardState actions', () => {
|
||||
const mockState = {
|
||||
dashboardState: {
|
||||
sliceIds: [filterId],
|
||||
hasUnsavedChanges: true,
|
||||
},
|
||||
dashboardInfo: {},
|
||||
sliceEntities,
|
||||
dashboardFilters: emptyFilters,
|
||||
dashboardLayout: {
|
||||
past: [],
|
||||
present: mockDashboardData.positions,
|
||||
future: {},
|
||||
},
|
||||
};
|
||||
const newDashboardData = mockDashboardData;
|
||||
|
||||
let postStub;
|
||||
beforeEach(() => {
|
||||
postStub = sinon
|
||||
.stub(SupersetClient, 'post')
|
||||
.resolves('the value you want to return');
|
||||
});
|
||||
afterEach(() => {
|
||||
postStub.restore();
|
||||
});
|
||||
|
||||
function setup(stateOverrides) {
|
||||
const state = { ...mockState, ...stateOverrides };
|
||||
const getState = sinon.spy(() => state);
|
||||
const dispatch = sinon.stub();
|
||||
return { getState, dispatch, state };
|
||||
}
|
||||
|
||||
describe('saveDashboardRequest', () => {
|
||||
it('should dispatch UPDATE_COMPONENTS_PARENTS_LIST action', () => {
|
||||
const { getState, dispatch } = setup({
|
||||
dashboardState: { hasUnsavedChanges: false },
|
||||
});
|
||||
const thunk = saveDashboardRequest(newDashboardData, 1, 'save_dash');
|
||||
thunk(dispatch, getState);
|
||||
expect(dispatch.callCount).toBe(1);
|
||||
expect(dispatch.getCall(0).args[0].type).toBe(
|
||||
UPDATE_COMPONENTS_PARENTS_LIST,
|
||||
);
|
||||
});
|
||||
|
||||
it('should post dashboard data with updated redux state', () => {
|
||||
const { getState, dispatch } = setup({
|
||||
dashboardState: { hasUnsavedChanges: false },
|
||||
});
|
||||
|
||||
// start with mockDashboardData, it didn't have parents attr
|
||||
expect(
|
||||
newDashboardData.positions[DASHBOARD_GRID_ID].parents,
|
||||
).not.toBeDefined();
|
||||
|
||||
// mock redux work: dispatch an event, cause modify redux state
|
||||
const mockParentsList = ['ROOT_ID'];
|
||||
dispatch.callsFake(() => {
|
||||
mockState.dashboardLayout.present[DASHBOARD_GRID_ID].parents =
|
||||
mockParentsList;
|
||||
});
|
||||
|
||||
// call saveDashboardRequest, it should post dashboard data with updated
|
||||
// layout object (with parents attribute)
|
||||
const thunk = saveDashboardRequest(newDashboardData, 1, 'save_dash');
|
||||
thunk(dispatch, getState);
|
||||
expect(postStub.callCount).toBe(1);
|
||||
const { postPayload } = postStub.getCall(0).args[0];
|
||||
expect(postPayload.data.positions[DASHBOARD_GRID_ID].parents).toBe(
|
||||
mockParentsList,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('should dispatch removeFilter if a removed slice is a filter_box', () => {
|
||||
const { getState, dispatch } = setup(mockState);
|
||||
const thunk = removeSliceFromDashboard(filterId);
|
||||
thunk(dispatch, getState);
|
||||
|
||||
const removeFilter = dispatch.getCall(0).args[0];
|
||||
removeFilter(dispatch, getState);
|
||||
expect(dispatch.getCall(3).args[0].type).toBe(REMOVE_FILTER);
|
||||
});
|
||||
});
|
||||
@@ -1,103 +0,0 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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 React from 'react';
|
||||
import { shallow, mount } from 'enzyme';
|
||||
import sinon from 'sinon';
|
||||
|
||||
import newComponentFactory from 'src/dashboard/util/newComponentFactory';
|
||||
import { CHART_TYPE, ROW_TYPE } from 'src/dashboard/util/componentTypes';
|
||||
import { UnwrappedDragDroppable as DragDroppable } from 'src/dashboard/components/dnd/DragDroppable';
|
||||
|
||||
describe('DragDroppable', () => {
|
||||
const props = {
|
||||
component: newComponentFactory(CHART_TYPE),
|
||||
parentComponent: newComponentFactory(ROW_TYPE),
|
||||
editMode: false,
|
||||
depth: 1,
|
||||
index: 0,
|
||||
isDragging: false,
|
||||
isDraggingOver: false,
|
||||
isDraggingOverShallow: false,
|
||||
droppableRef() {},
|
||||
dragSourceRef() {},
|
||||
dragPreviewRef() {},
|
||||
};
|
||||
|
||||
function setup(overrideProps, shouldMount = false) {
|
||||
const method = shouldMount ? mount : shallow;
|
||||
const wrapper = method(<DragDroppable {...props} {...overrideProps} />);
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
it('should render a div with class dragdroppable', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find('.dragdroppable')).toExist();
|
||||
});
|
||||
|
||||
it('should add class dragdroppable--dragging when dragging', () => {
|
||||
const wrapper = setup({ isDragging: true });
|
||||
expect(wrapper.find('.dragdroppable')).toExist();
|
||||
});
|
||||
|
||||
it('should call its child function', () => {
|
||||
const childrenSpy = sinon.spy();
|
||||
setup({ children: childrenSpy });
|
||||
expect(childrenSpy.callCount).toBe(1);
|
||||
});
|
||||
|
||||
it('should call its child function with "dragSourceRef" if editMode=true', () => {
|
||||
const children = sinon.spy();
|
||||
const dragSourceRef = () => {};
|
||||
setup({ children, editMode: false, dragSourceRef });
|
||||
setup({ children, editMode: true, dragSourceRef });
|
||||
|
||||
expect(children.getCall(0).args[0].dragSourceRef).toBeUndefined();
|
||||
expect(children.getCall(1).args[0].dragSourceRef).toBe(dragSourceRef);
|
||||
});
|
||||
|
||||
it('should call its child function with "dropIndicatorProps" dependent on editMode, isDraggingOver, state.dropIndicator is set', () => {
|
||||
const children = sinon.spy();
|
||||
const wrapper = setup({ children, editMode: false, isDraggingOver: false });
|
||||
wrapper.setState({ dropIndicator: 'nonsense' });
|
||||
wrapper.setProps({ ...props, editMode: true, isDraggingOver: true });
|
||||
|
||||
expect(children.callCount).toBe(3); // initial + setState + setProps
|
||||
expect(children.getCall(0).args[0].dropIndicatorProps).toBeUndefined();
|
||||
expect(children.getCall(2).args[0].dropIndicatorProps).toEqual({
|
||||
className: 'drop-indicator',
|
||||
});
|
||||
});
|
||||
|
||||
it('should call props.dragPreviewRef and props.droppableRef on mount', () => {
|
||||
const dragPreviewRef = sinon.spy();
|
||||
const droppableRef = sinon.spy();
|
||||
|
||||
setup({ dragPreviewRef, droppableRef }, true);
|
||||
expect(dragPreviewRef.callCount).toBe(1);
|
||||
expect(droppableRef.callCount).toBe(1);
|
||||
});
|
||||
|
||||
it('should set this.mounted dependent on life cycle', () => {
|
||||
const wrapper = setup({}, true);
|
||||
const instance = wrapper.instance();
|
||||
expect(instance.mounted).toBe(true);
|
||||
wrapper.unmount();
|
||||
expect(instance.mounted).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -1,143 +0,0 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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 { Provider } from 'react-redux';
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import sinon from 'sinon';
|
||||
import { supersetTheme, ThemeProvider } from '@superset-ui/core';
|
||||
import { DndProvider } from 'react-dnd';
|
||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||
|
||||
import Chart from 'src/dashboard/containers/Chart';
|
||||
import ChartHolderConnected from 'src/dashboard/components/gridComponents/ChartHolder';
|
||||
import DeleteComponentButton from 'src/dashboard/components/DeleteComponentButton';
|
||||
import DragDroppable from 'src/dashboard/components/dnd/DragDroppable';
|
||||
import HoverMenu from 'src/dashboard/components/menu/HoverMenu';
|
||||
import ResizableContainer from 'src/dashboard/components/resizable/ResizableContainer';
|
||||
|
||||
import { getMockStore } from 'spec/fixtures/mockStore';
|
||||
import { sliceId } from 'spec/fixtures/mockChartQueries';
|
||||
import dashboardInfo from 'spec/fixtures/mockDashboardInfo';
|
||||
import { dashboardLayout as mockLayout } from 'spec/fixtures/mockDashboardLayout';
|
||||
import { sliceEntitiesForChart } from 'spec/fixtures/mockSliceEntities';
|
||||
import { initialState } from 'src/SqlLab/fixtures';
|
||||
import { nativeFiltersInfo } from '../../fixtures/mockNativeFilters';
|
||||
|
||||
describe('ChartHolder', () => {
|
||||
const props = {
|
||||
id: String(sliceId),
|
||||
dashboardId: dashboardInfo.id,
|
||||
parentId: 'ROW_ID',
|
||||
component: mockLayout.present.CHART_ID,
|
||||
depth: 2,
|
||||
parentComponent: mockLayout.present.ROW_ID,
|
||||
index: 0,
|
||||
editMode: false,
|
||||
availableColumnCount: 12,
|
||||
columnWidth: 50,
|
||||
onResizeStart() {},
|
||||
onResize() {},
|
||||
onResizeStop() {},
|
||||
handleComponentDrop() {},
|
||||
updateComponents() {},
|
||||
deleteComponent() {},
|
||||
nativeFilters: nativeFiltersInfo.filters,
|
||||
};
|
||||
|
||||
function setup(overrideProps) {
|
||||
const mockStore = getMockStore({
|
||||
...initialState,
|
||||
sliceEntities: sliceEntitiesForChart,
|
||||
});
|
||||
|
||||
// We have to wrap provide DragDropContext for the underlying DragDroppable
|
||||
// otherwise we cannot assert on DragDroppable children
|
||||
const wrapper = mount(
|
||||
<Provider store={mockStore}>
|
||||
<DndProvider backend={HTML5Backend}>
|
||||
<ChartHolderConnected {...props} {...overrideProps} />
|
||||
</DndProvider>
|
||||
</Provider>,
|
||||
{
|
||||
wrappingComponent: ThemeProvider,
|
||||
wrappingComponentProps: { theme: supersetTheme },
|
||||
},
|
||||
);
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
it('should render a DragDroppable', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find(DragDroppable)).toExist();
|
||||
});
|
||||
|
||||
it('should render a ResizableContainer', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find(ResizableContainer)).toExist();
|
||||
});
|
||||
|
||||
it('should only have an adjustableWidth if its parent is a Row', () => {
|
||||
let wrapper = setup();
|
||||
expect(wrapper.find(ResizableContainer).prop('adjustableWidth')).toBe(true);
|
||||
|
||||
wrapper = setup({ ...props, parentComponent: mockLayout.present.CHART_ID });
|
||||
expect(wrapper.find(ResizableContainer).prop('adjustableWidth')).toBe(
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
it('should pass correct props to ResizableContainer', () => {
|
||||
const wrapper = setup();
|
||||
const resizableProps = wrapper.find(ResizableContainer).props();
|
||||
expect(resizableProps.widthStep).toBe(props.columnWidth);
|
||||
expect(resizableProps.widthMultiple).toBe(props.component.meta.width);
|
||||
expect(resizableProps.heightMultiple).toBe(props.component.meta.height);
|
||||
expect(resizableProps.maxWidthMultiple).toBe(
|
||||
props.component.meta.width + props.availableColumnCount,
|
||||
);
|
||||
});
|
||||
|
||||
it('should render a div with class "dashboard-component-chart-holder"', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find('.dashboard-component-chart-holder')).toExist();
|
||||
});
|
||||
|
||||
it('should render a Chart', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find(Chart)).toExist();
|
||||
});
|
||||
|
||||
it('should render a HoverMenu with DeleteComponentButton in editMode', () => {
|
||||
let wrapper = setup();
|
||||
expect(wrapper.find(HoverMenu)).not.toExist();
|
||||
expect(wrapper.find(DeleteComponentButton)).not.toExist();
|
||||
|
||||
// we cannot set props on the Divider because of the WithDragDropContext wrapper
|
||||
wrapper = setup({ editMode: true });
|
||||
expect(wrapper.find(HoverMenu)).toExist();
|
||||
expect(wrapper.find(DeleteComponentButton)).toExist();
|
||||
});
|
||||
|
||||
it('should call deleteComponent when deleted', () => {
|
||||
const deleteComponent = sinon.spy();
|
||||
const wrapper = setup({ editMode: true, deleteComponent });
|
||||
wrapper.find(DeleteComponentButton).simulate('click');
|
||||
expect(deleteComponent.callCount).toBe(1);
|
||||
});
|
||||
});
|
||||
@@ -1,135 +0,0 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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 React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import sinon from 'sinon';
|
||||
|
||||
import Chart from 'src/dashboard/components/gridComponents/Chart';
|
||||
import SliceHeader from 'src/dashboard/components/SliceHeader';
|
||||
import ChartContainer from 'src/chart/ChartContainer';
|
||||
import * as exploreUtils from 'src/explore/exploreUtils';
|
||||
import { sliceEntitiesForChart as sliceEntities } from 'spec/fixtures/mockSliceEntities';
|
||||
import mockDatasource from 'spec/fixtures/mockDatasource';
|
||||
import chartQueries, {
|
||||
sliceId as queryId,
|
||||
} from 'spec/fixtures/mockChartQueries';
|
||||
|
||||
describe('Chart', () => {
|
||||
const props = {
|
||||
id: queryId,
|
||||
width: 100,
|
||||
height: 100,
|
||||
updateSliceName() {},
|
||||
|
||||
// from redux
|
||||
maxRows: 666,
|
||||
chart: chartQueries[queryId],
|
||||
formData: chartQueries[queryId].formData,
|
||||
datasource: mockDatasource[sliceEntities.slices[queryId].datasource],
|
||||
slice: {
|
||||
...sliceEntities.slices[queryId],
|
||||
description_markeddown: 'markdown',
|
||||
owners: [],
|
||||
},
|
||||
sliceName: sliceEntities.slices[queryId].slice_name,
|
||||
timeout: 60,
|
||||
filters: {},
|
||||
refreshChart() {},
|
||||
toggleExpandSlice() {},
|
||||
addFilter() {},
|
||||
logEvent() {},
|
||||
handleToggleFullSize() {},
|
||||
changeFilter() {},
|
||||
setFocusedFilterField() {},
|
||||
unsetFocusedFilterField() {},
|
||||
addSuccessToast() {},
|
||||
addDangerToast() {},
|
||||
exportCSV() {},
|
||||
exportFullCSV() {},
|
||||
componentId: 'test',
|
||||
dashboardId: 111,
|
||||
editMode: false,
|
||||
isExpanded: false,
|
||||
supersetCanExplore: false,
|
||||
supersetCanCSV: false,
|
||||
sliceCanEdit: false,
|
||||
};
|
||||
|
||||
function setup(overrideProps) {
|
||||
const wrapper = shallow(<Chart {...props} {...overrideProps} />);
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
it('should render a SliceHeader', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find(SliceHeader)).toExist();
|
||||
});
|
||||
|
||||
it('should render a ChartContainer', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find(ChartContainer)).toExist();
|
||||
});
|
||||
|
||||
it('should render a description if it has one and isExpanded=true', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find('.slice_description')).not.toExist();
|
||||
wrapper.setProps({ ...props, isExpanded: true });
|
||||
expect(wrapper.find('.slice_description')).toExist();
|
||||
});
|
||||
|
||||
it('should call refreshChart when SliceHeader calls forceRefresh', () => {
|
||||
const refreshChart = sinon.spy();
|
||||
const wrapper = setup({ refreshChart });
|
||||
wrapper.instance().forceRefresh();
|
||||
expect(refreshChart.callCount).toBe(1);
|
||||
});
|
||||
|
||||
it('should call changeFilter when ChartContainer calls changeFilter', () => {
|
||||
const changeFilter = sinon.spy();
|
||||
const wrapper = setup({ changeFilter });
|
||||
wrapper.instance().changeFilter();
|
||||
expect(changeFilter.callCount).toBe(1);
|
||||
});
|
||||
it('should call exportChart when exportCSV is clicked', () => {
|
||||
const stubbedExportCSV = sinon
|
||||
.stub(exploreUtils, 'exportChart')
|
||||
.returns(() => {});
|
||||
const wrapper = setup();
|
||||
wrapper.instance().exportCSV(props.slice.sliceId);
|
||||
expect(stubbedExportCSV.calledOnce).toBe(true);
|
||||
expect(stubbedExportCSV.lastCall.args[0]).toEqual(
|
||||
expect.objectContaining({
|
||||
formData: expect.anything(),
|
||||
resultType: 'full',
|
||||
resultFormat: 'csv',
|
||||
}),
|
||||
);
|
||||
exploreUtils.exportChart.restore();
|
||||
});
|
||||
it('should call exportChart with row_limit props.maxRows when exportFullCSV is clicked', () => {
|
||||
const stubbedExportCSV = sinon
|
||||
.stub(exploreUtils, 'exportChart')
|
||||
.returns(() => {});
|
||||
const wrapper = setup();
|
||||
wrapper.instance().exportFullCSV(props.slice.sliceId);
|
||||
expect(stubbedExportCSV.calledOnce).toBe(true);
|
||||
expect(stubbedExportCSV.lastCall.args[0].formData.row_limit).toEqual(666);
|
||||
exploreUtils.exportChart.restore();
|
||||
});
|
||||
});
|
||||
@@ -1,170 +0,0 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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 { Provider } from 'react-redux';
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import sinon from 'sinon';
|
||||
import { supersetTheme, ThemeProvider } from '@superset-ui/core';
|
||||
import { DndProvider } from 'react-dnd';
|
||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||
|
||||
import BackgroundStyleDropdown from 'src/dashboard/components/menu/BackgroundStyleDropdown';
|
||||
import Column from 'src/dashboard/components/gridComponents/Column';
|
||||
import DashboardComponent from 'src/dashboard/containers/DashboardComponent';
|
||||
import DeleteComponentButton from 'src/dashboard/components/DeleteComponentButton';
|
||||
import DragDroppable from 'src/dashboard/components/dnd/DragDroppable';
|
||||
import HoverMenu from 'src/dashboard/components/menu/HoverMenu';
|
||||
import IconButton from 'src/dashboard/components/IconButton';
|
||||
import ResizableContainer from 'src/dashboard/components/resizable/ResizableContainer';
|
||||
import WithPopoverMenu from 'src/dashboard/components/menu/WithPopoverMenu';
|
||||
|
||||
import { getMockStore } from 'spec/fixtures/mockStore';
|
||||
import { dashboardLayout as mockLayout } from 'spec/fixtures/mockDashboardLayout';
|
||||
import { initialState } from 'src/SqlLab/fixtures';
|
||||
|
||||
describe('Column', () => {
|
||||
const columnWithoutChildren = {
|
||||
...mockLayout.present.COLUMN_ID,
|
||||
children: [],
|
||||
};
|
||||
const props = {
|
||||
id: 'COLUMN_ID',
|
||||
parentId: 'ROW_ID',
|
||||
component: mockLayout.present.COLUMN_ID,
|
||||
parentComponent: mockLayout.present.ROW_ID,
|
||||
index: 0,
|
||||
depth: 2,
|
||||
editMode: false,
|
||||
availableColumnCount: 12,
|
||||
minColumnWidth: 2,
|
||||
columnWidth: 50,
|
||||
occupiedColumnCount: 6,
|
||||
onResizeStart() {},
|
||||
onResize() {},
|
||||
onResizeStop() {},
|
||||
handleComponentDrop() {},
|
||||
deleteComponent() {},
|
||||
updateComponents() {},
|
||||
};
|
||||
|
||||
function setup(overrideProps) {
|
||||
// We have to wrap provide DragDropContext for the underlying DragDroppable
|
||||
// otherwise we cannot assert on DragDroppable children
|
||||
const mockStore = getMockStore({
|
||||
...initialState,
|
||||
});
|
||||
const wrapper = mount(
|
||||
<Provider store={mockStore}>
|
||||
<DndProvider backend={HTML5Backend}>
|
||||
<Column {...props} {...overrideProps} />
|
||||
</DndProvider>
|
||||
</Provider>,
|
||||
{
|
||||
wrappingComponent: ThemeProvider,
|
||||
wrappingComponentProps: { theme: supersetTheme },
|
||||
},
|
||||
);
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
it('should render a DragDroppable', () => {
|
||||
// don't count child DragDroppables
|
||||
const wrapper = setup({ component: columnWithoutChildren });
|
||||
expect(wrapper.find(DragDroppable)).toExist();
|
||||
});
|
||||
|
||||
it('should render a WithPopoverMenu', () => {
|
||||
// don't count child DragDroppables
|
||||
const wrapper = setup({ component: columnWithoutChildren });
|
||||
expect(wrapper.find(WithPopoverMenu)).toExist();
|
||||
});
|
||||
|
||||
it('should render a ResizableContainer', () => {
|
||||
// don't count child DragDroppables
|
||||
const wrapper = setup({ component: columnWithoutChildren });
|
||||
expect(wrapper.find(ResizableContainer)).toExist();
|
||||
});
|
||||
|
||||
it('should render a HoverMenu in editMode', () => {
|
||||
let wrapper = setup({ component: columnWithoutChildren });
|
||||
expect(wrapper.find(HoverMenu)).not.toExist();
|
||||
|
||||
// we cannot set props on the Row because of the WithDragDropContext wrapper
|
||||
wrapper = setup({ component: columnWithoutChildren, editMode: true });
|
||||
expect(wrapper.find(HoverMenu)).toExist();
|
||||
});
|
||||
|
||||
it('should render a DeleteComponentButton in editMode', () => {
|
||||
let wrapper = setup({ component: columnWithoutChildren });
|
||||
expect(wrapper.find(DeleteComponentButton)).not.toExist();
|
||||
|
||||
// we cannot set props on the Row because of the WithDragDropContext wrapper
|
||||
wrapper = setup({ component: columnWithoutChildren, editMode: true });
|
||||
expect(wrapper.find(DeleteComponentButton)).toExist();
|
||||
});
|
||||
|
||||
it('should render a BackgroundStyleDropdown when focused', () => {
|
||||
let wrapper = setup({ component: columnWithoutChildren });
|
||||
expect(wrapper.find(BackgroundStyleDropdown)).not.toExist();
|
||||
|
||||
// we cannot set props on the Row because of the WithDragDropContext wrapper
|
||||
wrapper = setup({ component: columnWithoutChildren, editMode: true });
|
||||
wrapper
|
||||
.find(IconButton)
|
||||
.at(1) // first one is delete button
|
||||
.simulate('click');
|
||||
|
||||
expect(wrapper.find(BackgroundStyleDropdown)).toExist();
|
||||
});
|
||||
|
||||
it('should call deleteComponent when deleted', () => {
|
||||
const deleteComponent = sinon.spy();
|
||||
const wrapper = setup({ editMode: true, deleteComponent });
|
||||
wrapper.find(DeleteComponentButton).simulate('click');
|
||||
expect(deleteComponent.callCount).toBe(1);
|
||||
});
|
||||
|
||||
it('should pass its own width as availableColumnCount to children', () => {
|
||||
const wrapper = setup();
|
||||
const dashboardComponent = wrapper.find(DashboardComponent).first();
|
||||
expect(dashboardComponent.props().availableColumnCount).toBe(
|
||||
props.component.meta.width,
|
||||
);
|
||||
});
|
||||
|
||||
it('should pass appropriate dimensions to ResizableContainer', () => {
|
||||
const wrapper = setup({ component: columnWithoutChildren });
|
||||
const columnWidth = columnWithoutChildren.meta.width;
|
||||
const resizableProps = wrapper.find(ResizableContainer).props();
|
||||
expect(resizableProps.adjustableWidth).toBe(true);
|
||||
expect(resizableProps.adjustableHeight).toBe(false);
|
||||
expect(resizableProps.widthStep).toBe(props.columnWidth);
|
||||
expect(resizableProps.widthMultiple).toBe(columnWidth);
|
||||
expect(resizableProps.minWidthMultiple).toBe(props.minColumnWidth);
|
||||
expect(resizableProps.maxWidthMultiple).toBe(
|
||||
props.availableColumnCount + columnWidth,
|
||||
);
|
||||
});
|
||||
|
||||
it('should increment the depth of its children', () => {
|
||||
const wrapper = setup();
|
||||
const dashboardComponent = wrapper.find(DashboardComponent);
|
||||
expect(dashboardComponent.props().depth).toBe(props.depth + 1);
|
||||
});
|
||||
});
|
||||
@@ -1,86 +0,0 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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 React from 'react';
|
||||
import { styledMount as mount } from 'spec/helpers/theming';
|
||||
import sinon from 'sinon';
|
||||
import { DndProvider } from 'react-dnd';
|
||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||
|
||||
import DeleteComponentButton from 'src/dashboard/components/DeleteComponentButton';
|
||||
import HoverMenu from 'src/dashboard/components/menu/HoverMenu';
|
||||
import DragDroppable from 'src/dashboard/components/dnd/DragDroppable';
|
||||
import Divider from 'src/dashboard/components/gridComponents/Divider';
|
||||
import newComponentFactory from 'src/dashboard/util/newComponentFactory';
|
||||
import {
|
||||
DIVIDER_TYPE,
|
||||
DASHBOARD_GRID_TYPE,
|
||||
} from 'src/dashboard/util/componentTypes';
|
||||
|
||||
describe('Divider', () => {
|
||||
const props = {
|
||||
id: 'id',
|
||||
parentId: 'parentId',
|
||||
component: newComponentFactory(DIVIDER_TYPE),
|
||||
depth: 1,
|
||||
parentComponent: newComponentFactory(DASHBOARD_GRID_TYPE),
|
||||
index: 0,
|
||||
editMode: false,
|
||||
handleComponentDrop() {},
|
||||
deleteComponent() {},
|
||||
};
|
||||
|
||||
function setup(overrideProps) {
|
||||
// We have to wrap provide DragDropContext for the underlying DragDroppable
|
||||
// otherwise we cannot assert on DragDroppable children
|
||||
const wrapper = mount(
|
||||
<DndProvider backend={HTML5Backend}>
|
||||
<Divider {...props} {...overrideProps} />
|
||||
</DndProvider>,
|
||||
);
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
it('should render a DragDroppable', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find(DragDroppable)).toExist();
|
||||
});
|
||||
|
||||
it('should render a div with class "dashboard-component-divider"', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find('.dashboard-component-divider')).toExist();
|
||||
});
|
||||
|
||||
it('should render a HoverMenu with DeleteComponentButton in editMode', () => {
|
||||
let wrapper = setup();
|
||||
expect(wrapper.find(HoverMenu)).not.toExist();
|
||||
expect(wrapper.find(DeleteComponentButton)).not.toExist();
|
||||
|
||||
// we cannot set props on the Divider because of the WithDragDropContext wrapper
|
||||
wrapper = setup({ editMode: true });
|
||||
expect(wrapper.find(HoverMenu)).toExist();
|
||||
expect(wrapper.find(DeleteComponentButton)).toExist();
|
||||
});
|
||||
|
||||
it('should call deleteComponent when deleted', () => {
|
||||
const deleteComponent = sinon.spy();
|
||||
const wrapper = setup({ editMode: true, deleteComponent });
|
||||
wrapper.find(DeleteComponentButton).simulate('click');
|
||||
expect(deleteComponent.callCount).toBe(1);
|
||||
});
|
||||
});
|
||||
@@ -1,122 +0,0 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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 React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import { styledMount as mount } from 'spec/helpers/theming';
|
||||
import sinon from 'sinon';
|
||||
import { DndProvider } from 'react-dnd';
|
||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||
|
||||
import DeleteComponentButton from 'src/dashboard/components/DeleteComponentButton';
|
||||
import EditableTitle from 'src/components/EditableTitle';
|
||||
import HoverMenu from 'src/dashboard/components/menu/HoverMenu';
|
||||
import WithPopoverMenu from 'src/dashboard/components/menu/WithPopoverMenu';
|
||||
import DragDroppable from 'src/dashboard/components/dnd/DragDroppable';
|
||||
import Header from 'src/dashboard/components/gridComponents/Header';
|
||||
import newComponentFactory from 'src/dashboard/util/newComponentFactory';
|
||||
import {
|
||||
HEADER_TYPE,
|
||||
DASHBOARD_GRID_TYPE,
|
||||
} from 'src/dashboard/util/componentTypes';
|
||||
|
||||
import { mockStoreWithTabs } from 'spec/fixtures/mockStore';
|
||||
|
||||
describe('Header', () => {
|
||||
const props = {
|
||||
id: 'id',
|
||||
parentId: 'parentId',
|
||||
component: newComponentFactory(HEADER_TYPE),
|
||||
depth: 1,
|
||||
parentComponent: newComponentFactory(DASHBOARD_GRID_TYPE),
|
||||
index: 0,
|
||||
editMode: false,
|
||||
filters: {},
|
||||
handleComponentDrop() {},
|
||||
deleteComponent() {},
|
||||
updateComponents() {},
|
||||
};
|
||||
|
||||
function setup(overrideProps) {
|
||||
// We have to wrap provide DragDropContext for the underlying DragDroppable
|
||||
// otherwise we cannot assert on DragDroppable children
|
||||
const wrapper = mount(
|
||||
<Provider store={mockStoreWithTabs}>
|
||||
<DndProvider backend={HTML5Backend}>
|
||||
<Header {...props} {...overrideProps} />
|
||||
</DndProvider>
|
||||
</Provider>,
|
||||
);
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
it('should render a DragDroppable', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find(DragDroppable)).toExist();
|
||||
});
|
||||
|
||||
it('should render a WithPopoverMenu', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find(WithPopoverMenu)).toExist();
|
||||
});
|
||||
|
||||
it('should render a HoverMenu in editMode', () => {
|
||||
let wrapper = setup();
|
||||
expect(wrapper.find(HoverMenu)).not.toExist();
|
||||
|
||||
// we cannot set props on the Header because of the WithDragDropContext wrapper
|
||||
wrapper = setup({ editMode: true });
|
||||
expect(wrapper.find(HoverMenu)).toExist();
|
||||
});
|
||||
|
||||
it('should render an EditableTitle with meta.text', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find(EditableTitle)).toExist();
|
||||
expect(wrapper.find('.editable-title')).toHaveText(
|
||||
props.component.meta.text,
|
||||
);
|
||||
});
|
||||
|
||||
it('should call updateComponents when EditableTitle changes', () => {
|
||||
const updateComponents = sinon.spy();
|
||||
const wrapper = setup({ editMode: true, updateComponents });
|
||||
wrapper.find(EditableTitle).prop('onSaveTitle')('New title');
|
||||
|
||||
const headerId = props.component.id;
|
||||
expect(updateComponents.callCount).toBe(1);
|
||||
expect(updateComponents.getCall(0).args[0][headerId].meta.text).toBe(
|
||||
'New title',
|
||||
);
|
||||
});
|
||||
|
||||
it('should render a DeleteComponentButton when focused in editMode', () => {
|
||||
const wrapper = setup({ editMode: true });
|
||||
wrapper.find(WithPopoverMenu).simulate('click'); // focus
|
||||
|
||||
expect(wrapper.find(DeleteComponentButton)).toExist();
|
||||
});
|
||||
|
||||
it('should call deleteComponent when deleted', () => {
|
||||
const deleteComponent = sinon.spy();
|
||||
const wrapper = setup({ editMode: true, deleteComponent });
|
||||
wrapper.find(WithPopoverMenu).simulate('click'); // focus
|
||||
wrapper.find(DeleteComponentButton).simulate('click');
|
||||
|
||||
expect(deleteComponent.callCount).toBe(1);
|
||||
});
|
||||
});
|
||||
@@ -1,181 +0,0 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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 { Provider } from 'react-redux';
|
||||
import React from 'react';
|
||||
import { styledMount as mount } from 'spec/helpers/theming';
|
||||
import sinon from 'sinon';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import { DndProvider } from 'react-dnd';
|
||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { MarkdownEditor } from 'src/components/AsyncAceEditor';
|
||||
import MarkdownConnected from 'src/dashboard/components/gridComponents/Markdown';
|
||||
import MarkdownModeDropdown from 'src/dashboard/components/menu/MarkdownModeDropdown';
|
||||
import DeleteComponentButton from 'src/dashboard/components/DeleteComponentButton';
|
||||
import waitForComponentToPaint from 'spec/helpers/waitForComponentToPaint';
|
||||
import DragDroppable from 'src/dashboard/components/dnd/DragDroppable';
|
||||
import WithPopoverMenu from 'src/dashboard/components/menu/WithPopoverMenu';
|
||||
import ResizableContainer from 'src/dashboard/components/resizable/ResizableContainer';
|
||||
|
||||
import { mockStore } from 'spec/fixtures/mockStore';
|
||||
import { dashboardLayout as mockLayout } from 'spec/fixtures/mockDashboardLayout';
|
||||
|
||||
describe('Markdown', () => {
|
||||
const props = {
|
||||
id: 'id',
|
||||
parentId: 'parentId',
|
||||
component: mockLayout.present.MARKDOWN_ID,
|
||||
depth: 2,
|
||||
parentComponent: mockLayout.present.ROW_ID,
|
||||
index: 0,
|
||||
editMode: false,
|
||||
availableColumnCount: 12,
|
||||
columnWidth: 50,
|
||||
redoLength: 0,
|
||||
undoLength: 0,
|
||||
onResizeStart() {},
|
||||
onResize() {},
|
||||
onResizeStop() {},
|
||||
handleComponentDrop() {},
|
||||
updateComponents() {},
|
||||
deleteComponent() {},
|
||||
logEvent() {},
|
||||
addDangerToast() {},
|
||||
};
|
||||
|
||||
function setup(overrideProps) {
|
||||
// We have to wrap provide DragDropContext for the underlying DragDroppable
|
||||
// otherwise we cannot assert on DragDroppable children
|
||||
const wrapper = mount(
|
||||
<Provider store={mockStore}>
|
||||
<DndProvider backend={HTML5Backend}>
|
||||
<MarkdownConnected {...props} {...overrideProps} />
|
||||
</DndProvider>
|
||||
</Provider>,
|
||||
);
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
it('should render a DragDroppable', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find(DragDroppable)).toExist();
|
||||
});
|
||||
|
||||
it('should render a WithPopoverMenu', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find(WithPopoverMenu)).toExist();
|
||||
});
|
||||
|
||||
it('should render a ResizableContainer', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find(ResizableContainer)).toExist();
|
||||
});
|
||||
|
||||
it('should only have an adjustableWidth if its parent is a Row', () => {
|
||||
let wrapper = setup();
|
||||
expect(wrapper.find(ResizableContainer).prop('adjustableWidth')).toBe(true);
|
||||
|
||||
wrapper = setup({ ...props, parentComponent: mockLayout.present.CHART_ID });
|
||||
expect(wrapper.find(ResizableContainer).prop('adjustableWidth')).toBe(
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
it('should pass correct props to ResizableContainer', () => {
|
||||
const wrapper = setup();
|
||||
const resizableProps = wrapper.find(ResizableContainer).props();
|
||||
expect(resizableProps.widthStep).toBe(props.columnWidth);
|
||||
expect(resizableProps.widthMultiple).toBe(props.component.meta.width);
|
||||
expect(resizableProps.heightMultiple).toBe(props.component.meta.height);
|
||||
expect(resizableProps.maxWidthMultiple).toBe(
|
||||
props.component.meta.width + props.availableColumnCount,
|
||||
);
|
||||
});
|
||||
|
||||
it('should render an Markdown when NOT focused', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find(MarkdownEditor)).not.toExist();
|
||||
expect(wrapper.find(ReactMarkdown)).toExist();
|
||||
});
|
||||
|
||||
it('should render an AceEditor when focused and editMode=true and editorMode=edit', async () => {
|
||||
const wrapper = setup({ editMode: true });
|
||||
expect(wrapper.find(MarkdownEditor)).not.toExist();
|
||||
expect(wrapper.find(ReactMarkdown)).toExist();
|
||||
act(() => {
|
||||
wrapper.find(WithPopoverMenu).simulate('click'); // focus + edit
|
||||
});
|
||||
await waitForComponentToPaint(wrapper);
|
||||
expect(wrapper.find(MarkdownEditor)).toExist();
|
||||
expect(wrapper.find(ReactMarkdown)).not.toExist();
|
||||
});
|
||||
|
||||
it('should render a ReactMarkdown when focused and editMode=true and editorMode=preview', () => {
|
||||
const wrapper = setup({ editMode: true });
|
||||
wrapper.find(WithPopoverMenu).simulate('click'); // focus + edit
|
||||
expect(wrapper.find(MarkdownEditor)).toExist();
|
||||
expect(wrapper.find(ReactMarkdown)).not.toExist();
|
||||
|
||||
// we can't call setState on Markdown bc it's not the root component, so call
|
||||
// the mode dropdown onchange instead
|
||||
const dropdown = wrapper.find(MarkdownModeDropdown);
|
||||
dropdown.prop('onChange')('preview');
|
||||
wrapper.update();
|
||||
|
||||
expect(wrapper.find(ReactMarkdown)).toExist();
|
||||
expect(wrapper.find(MarkdownEditor)).not.toExist();
|
||||
});
|
||||
|
||||
it('should call updateComponents when editMode changes from edit => preview, and there are markdownSource changes', () => {
|
||||
const updateComponents = sinon.spy();
|
||||
const wrapper = setup({ editMode: true, updateComponents });
|
||||
wrapper.find(WithPopoverMenu).simulate('click'); // focus + edit
|
||||
|
||||
// we can't call setState on Markdown bc it's not the root component, so call
|
||||
// the mode dropdown onchange instead
|
||||
const dropdown = wrapper.find(MarkdownModeDropdown);
|
||||
dropdown.prop('onChange')('preview');
|
||||
expect(updateComponents.callCount).toBe(0);
|
||||
|
||||
dropdown.prop('onChange')('edit');
|
||||
// because we can't call setState on Markdown, change it through the editor
|
||||
// then go back to preview mode to invoke updateComponents
|
||||
const editor = wrapper.find(MarkdownEditor);
|
||||
editor.prop('onChange')('new markdown!');
|
||||
dropdown.prop('onChange')('preview');
|
||||
expect(updateComponents.callCount).toBe(1);
|
||||
});
|
||||
|
||||
it('should render a DeleteComponentButton when focused in editMode', () => {
|
||||
const wrapper = setup({ editMode: true });
|
||||
wrapper.find(WithPopoverMenu).simulate('click'); // focus
|
||||
|
||||
expect(wrapper.find(DeleteComponentButton)).toExist();
|
||||
});
|
||||
|
||||
it('should call deleteComponent when deleted', () => {
|
||||
const deleteComponent = sinon.spy();
|
||||
const wrapper = setup({ editMode: true, deleteComponent });
|
||||
wrapper.find(WithPopoverMenu).simulate('click'); // focus
|
||||
wrapper.find(DeleteComponentButton).simulate('click');
|
||||
|
||||
expect(deleteComponent.callCount).toBe(1);
|
||||
});
|
||||
});
|
||||
@@ -1,146 +0,0 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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 { Provider } from 'react-redux';
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import sinon from 'sinon';
|
||||
import { DndProvider } from 'react-dnd';
|
||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||
|
||||
import BackgroundStyleDropdown from 'src/dashboard/components/menu/BackgroundStyleDropdown';
|
||||
import DashboardComponent from 'src/dashboard/containers/DashboardComponent';
|
||||
import DeleteComponentButton from 'src/dashboard/components/DeleteComponentButton';
|
||||
import DragDroppable from 'src/dashboard/components/dnd/DragDroppable';
|
||||
import HoverMenu from 'src/dashboard/components/menu/HoverMenu';
|
||||
import IconButton from 'src/dashboard/components/IconButton';
|
||||
import Row from 'src/dashboard/components/gridComponents/Row';
|
||||
import WithPopoverMenu from 'src/dashboard/components/menu/WithPopoverMenu';
|
||||
import { DASHBOARD_GRID_ID } from 'src/dashboard/util/constants';
|
||||
import { supersetTheme, ThemeProvider } from '@superset-ui/core';
|
||||
|
||||
import { getMockStore } from 'spec/fixtures/mockStore';
|
||||
import { dashboardLayout as mockLayout } from 'spec/fixtures/mockDashboardLayout';
|
||||
import { initialState } from 'src/SqlLab/fixtures';
|
||||
|
||||
describe('Row', () => {
|
||||
const rowWithoutChildren = { ...mockLayout.present.ROW_ID, children: [] };
|
||||
const props = {
|
||||
id: 'ROW_ID',
|
||||
parentId: DASHBOARD_GRID_ID,
|
||||
component: mockLayout.present.ROW_ID,
|
||||
parentComponent: mockLayout.present[DASHBOARD_GRID_ID],
|
||||
index: 0,
|
||||
depth: 2,
|
||||
editMode: false,
|
||||
availableColumnCount: 12,
|
||||
columnWidth: 50,
|
||||
occupiedColumnCount: 6,
|
||||
onResizeStart() {},
|
||||
onResize() {},
|
||||
onResizeStop() {},
|
||||
handleComponentDrop() {},
|
||||
deleteComponent() {},
|
||||
updateComponents() {},
|
||||
};
|
||||
|
||||
function setup(overrideProps) {
|
||||
// We have to wrap provide DragDropContext for the underlying DragDroppable
|
||||
// otherwise we cannot assert on DragDroppable children
|
||||
const mockStore = getMockStore({
|
||||
...initialState,
|
||||
});
|
||||
const wrapper = mount(
|
||||
<Provider store={mockStore}>
|
||||
<DndProvider backend={HTML5Backend}>
|
||||
<Row {...props} {...overrideProps} />
|
||||
</DndProvider>
|
||||
</Provider>,
|
||||
{
|
||||
wrappingComponent: ThemeProvider,
|
||||
wrappingComponentProps: { theme: supersetTheme },
|
||||
},
|
||||
);
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
it('should render a DragDroppable', () => {
|
||||
// don't count child DragDroppables
|
||||
const wrapper = setup({ component: rowWithoutChildren });
|
||||
expect(wrapper.find(DragDroppable)).toExist();
|
||||
});
|
||||
|
||||
it('should render a WithPopoverMenu', () => {
|
||||
// don't count child DragDroppables
|
||||
const wrapper = setup({ component: rowWithoutChildren });
|
||||
expect(wrapper.find(WithPopoverMenu)).toExist();
|
||||
});
|
||||
|
||||
it('should render a HoverMenu in editMode', () => {
|
||||
let wrapper = setup({ component: rowWithoutChildren });
|
||||
expect(wrapper.find(HoverMenu)).not.toExist();
|
||||
|
||||
// we cannot set props on the Row because of the WithDragDropContext wrapper
|
||||
wrapper = setup({ component: rowWithoutChildren, editMode: true });
|
||||
expect(wrapper.find(HoverMenu)).toExist();
|
||||
});
|
||||
|
||||
it('should render a DeleteComponentButton in editMode', () => {
|
||||
let wrapper = setup({ component: rowWithoutChildren });
|
||||
expect(wrapper.find(DeleteComponentButton)).not.toExist();
|
||||
|
||||
// we cannot set props on the Row because of the WithDragDropContext wrapper
|
||||
wrapper = setup({ component: rowWithoutChildren, editMode: true });
|
||||
expect(wrapper.find(DeleteComponentButton)).toExist();
|
||||
});
|
||||
|
||||
it('should render a BackgroundStyleDropdown when focused', () => {
|
||||
let wrapper = setup({ component: rowWithoutChildren });
|
||||
expect(wrapper.find(BackgroundStyleDropdown)).not.toExist();
|
||||
|
||||
// we cannot set props on the Row because of the WithDragDropContext wrapper
|
||||
wrapper = setup({ component: rowWithoutChildren, editMode: true });
|
||||
wrapper
|
||||
.find(IconButton)
|
||||
.at(1) // first one is delete button
|
||||
.simulate('click');
|
||||
|
||||
expect(wrapper.find(BackgroundStyleDropdown)).toExist();
|
||||
});
|
||||
|
||||
it('should call deleteComponent when deleted', () => {
|
||||
const deleteComponent = sinon.spy();
|
||||
const wrapper = setup({ editMode: true, deleteComponent });
|
||||
wrapper.find(DeleteComponentButton).simulate('click');
|
||||
expect(deleteComponent.callCount).toBe(1);
|
||||
});
|
||||
|
||||
it('should pass appropriate availableColumnCount to children', () => {
|
||||
const wrapper = setup();
|
||||
const dashboardComponent = wrapper.find(DashboardComponent).first();
|
||||
expect(dashboardComponent.props().availableColumnCount).toBe(
|
||||
props.availableColumnCount - props.occupiedColumnCount,
|
||||
);
|
||||
});
|
||||
|
||||
it('should increment the depth of its children', () => {
|
||||
const wrapper = setup();
|
||||
const dashboardComponent = wrapper.find(DashboardComponent).first();
|
||||
expect(dashboardComponent.props().depth).toBe(props.depth + 1);
|
||||
});
|
||||
});
|
||||
@@ -1,115 +0,0 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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 { Provider } from 'react-redux';
|
||||
import React from 'react';
|
||||
import { styledMount as mount } from 'spec/helpers/theming';
|
||||
import sinon from 'sinon';
|
||||
import { DndProvider } from 'react-dnd';
|
||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||
|
||||
import DashboardComponent from 'src/dashboard/containers/DashboardComponent';
|
||||
import DragDroppable from 'src/dashboard/components/dnd/DragDroppable';
|
||||
import EditableTitle from 'src/components/EditableTitle';
|
||||
import Tab, {
|
||||
RENDER_TAB,
|
||||
RENDER_TAB_CONTENT,
|
||||
} from 'src/dashboard/components/gridComponents/Tab';
|
||||
import { dashboardLayoutWithTabs } from 'spec/fixtures/mockDashboardLayout';
|
||||
import { getMockStore } from 'spec/fixtures/mockStore';
|
||||
import { initialState } from 'src/SqlLab/fixtures';
|
||||
|
||||
describe('Tabs', () => {
|
||||
const props = {
|
||||
id: 'TAB_ID',
|
||||
parentId: 'TABS_ID',
|
||||
component: dashboardLayoutWithTabs.present.TAB_ID,
|
||||
parentComponent: dashboardLayoutWithTabs.present.TABS_ID,
|
||||
index: 0,
|
||||
depth: 1,
|
||||
editMode: false,
|
||||
renderType: RENDER_TAB,
|
||||
filters: {},
|
||||
setDirectPathToChild: jest.fn(),
|
||||
onDropOnTab() {},
|
||||
onDeleteTab() {},
|
||||
availableColumnCount: 12,
|
||||
columnWidth: 50,
|
||||
onResizeStart() {},
|
||||
onResize() {},
|
||||
onResizeStop() {},
|
||||
createComponent() {},
|
||||
handleComponentDrop() {},
|
||||
onChangeTab() {},
|
||||
deleteComponent() {},
|
||||
updateComponents() {},
|
||||
};
|
||||
|
||||
function setup(overrideProps) {
|
||||
// We have to wrap provide DragDropContext for the underlying DragDroppable
|
||||
// otherwise we cannot assert on DragDroppable children
|
||||
const mockStore = getMockStore({
|
||||
...initialState,
|
||||
dashboardLayout: dashboardLayoutWithTabs,
|
||||
dashboardFilters: {},
|
||||
});
|
||||
const wrapper = mount(
|
||||
<Provider store={mockStore}>
|
||||
<DndProvider backend={HTML5Backend}>
|
||||
<Tab {...props} {...overrideProps} />
|
||||
</DndProvider>
|
||||
</Provider>,
|
||||
);
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
describe('renderType=RENDER_TAB', () => {
|
||||
it('should render a DragDroppable', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find(DragDroppable)).toExist();
|
||||
});
|
||||
|
||||
it('should render an EditableTitle with meta.text', () => {
|
||||
const wrapper = setup();
|
||||
const title = wrapper.find(EditableTitle);
|
||||
expect(title).toHaveLength(1);
|
||||
expect(title.find('.editable-title')).toHaveText(
|
||||
props.component.meta.defaultText,
|
||||
);
|
||||
});
|
||||
|
||||
it('should call updateComponents when EditableTitle changes', () => {
|
||||
const updateComponents = sinon.spy();
|
||||
const wrapper = setup({ editMode: true, updateComponents });
|
||||
wrapper.find(EditableTitle).prop('onSaveTitle')('New title');
|
||||
|
||||
expect(updateComponents.callCount).toBe(1);
|
||||
expect(updateComponents.getCall(0).args[0].TAB_ID.meta.text).toBe(
|
||||
'New title',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('renderType=RENDER_TAB_CONTENT', () => {
|
||||
it('should render a DashboardComponent', () => {
|
||||
const wrapper = setup({ renderType: RENDER_TAB_CONTENT });
|
||||
// We expect 2 because this Tab has a Row child and the row has a Chart
|
||||
expect(wrapper.find(DashboardComponent)).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,205 +0,0 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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 { Provider } from 'react-redux';
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import sinon from 'sinon';
|
||||
import { DndProvider } from 'react-dnd';
|
||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||
|
||||
import { LineEditableTabs } from 'src/components/Tabs';
|
||||
import { Modal } from 'src/common/components';
|
||||
import fetchMock from 'fetch-mock';
|
||||
import { styledMount as mount } from 'spec/helpers/theming';
|
||||
import DashboardComponent from 'src/dashboard/containers/DashboardComponent';
|
||||
import DeleteComponentButton from 'src/dashboard/components/DeleteComponentButton';
|
||||
import HoverMenu from 'src/dashboard/components/menu/HoverMenu';
|
||||
import DragDroppable from 'src/dashboard/components/dnd/DragDroppable';
|
||||
import { Tabs } from 'src/dashboard/components/gridComponents/Tabs';
|
||||
import { DASHBOARD_ROOT_ID } from 'src/dashboard/util/constants';
|
||||
import emptyDashboardLayout from 'src/dashboard/fixtures/emptyDashboardLayout';
|
||||
import { dashboardLayoutWithTabs } from 'spec/fixtures/mockDashboardLayout';
|
||||
import { getMockStore } from 'spec/fixtures/mockStore';
|
||||
import { nativeFilters } from 'spec/fixtures/mockNativeFilters';
|
||||
import { initialState } from 'src/SqlLab/fixtures';
|
||||
|
||||
describe('Tabs', () => {
|
||||
fetchMock.post('glob:*/r/shortner/', {});
|
||||
|
||||
const props = {
|
||||
id: 'TABS_ID',
|
||||
parentId: DASHBOARD_ROOT_ID,
|
||||
component: dashboardLayoutWithTabs.present.TABS_ID,
|
||||
parentComponent: dashboardLayoutWithTabs.present[DASHBOARD_ROOT_ID],
|
||||
index: 0,
|
||||
depth: 1,
|
||||
renderTabContent: true,
|
||||
editMode: false,
|
||||
availableColumnCount: 12,
|
||||
columnWidth: 50,
|
||||
onResizeStart() {},
|
||||
onResize() {},
|
||||
onResizeStop() {},
|
||||
createComponent() {},
|
||||
handleComponentDrop() {},
|
||||
onChangeTab() {},
|
||||
deleteComponent() {},
|
||||
updateComponents() {},
|
||||
logEvent() {},
|
||||
dashboardLayout: emptyDashboardLayout,
|
||||
nativeFilters: nativeFilters.filters,
|
||||
};
|
||||
|
||||
const mockStore = getMockStore({
|
||||
...initialState,
|
||||
dashboardLayout: dashboardLayoutWithTabs,
|
||||
dashboardFilters: {},
|
||||
});
|
||||
|
||||
function setup(overrideProps) {
|
||||
// We have to wrap provide DragDropContext for the underlying DragDroppable
|
||||
// otherwise we cannot assert on DragDroppable children
|
||||
const wrapper = mount(
|
||||
<Provider store={mockStore}>
|
||||
<DndProvider backend={HTML5Backend}>
|
||||
<Tabs {...props} {...overrideProps} />
|
||||
</DndProvider>
|
||||
</Provider>,
|
||||
);
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
it('should render a DragDroppable', () => {
|
||||
// test just Tabs with no children DragDroppables
|
||||
const wrapper = setup({ component: { ...props.component, children: [] } });
|
||||
expect(wrapper.find(DragDroppable)).toExist();
|
||||
});
|
||||
|
||||
it('should render non-editable tabs', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find(LineEditableTabs)).toExist();
|
||||
expect(wrapper.find('.ant-tabs-nav-add').exists()).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should render a tab pane for each child', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find(LineEditableTabs.TabPane)).toHaveLength(
|
||||
props.component.children.length,
|
||||
);
|
||||
});
|
||||
|
||||
it('should render editable tabs in editMode', () => {
|
||||
const wrapper = setup({ editMode: true });
|
||||
expect(wrapper.find(LineEditableTabs)).toExist();
|
||||
expect(wrapper.find('.ant-tabs-nav-add')).toExist();
|
||||
});
|
||||
|
||||
it('should render a DashboardComponent for each child', () => {
|
||||
// note: this does not test Tab content
|
||||
const wrapper = setup({ renderTabContent: false });
|
||||
expect(wrapper.find(DashboardComponent)).toHaveLength(
|
||||
props.component.children.length,
|
||||
);
|
||||
});
|
||||
|
||||
it('should call createComponent if the (+) tab is clicked', () => {
|
||||
const createComponent = sinon.spy();
|
||||
const wrapper = setup({ editMode: true, createComponent });
|
||||
wrapper
|
||||
.find('[data-test="dashboard-component-tabs"] .ant-tabs-nav-add')
|
||||
.last()
|
||||
.simulate('click');
|
||||
|
||||
expect(createComponent.callCount).toBe(1);
|
||||
});
|
||||
|
||||
it('should call onChangeTab when a tab is clicked', () => {
|
||||
const onChangeTab = sinon.spy();
|
||||
const wrapper = setup({ editMode: true, onChangeTab });
|
||||
wrapper
|
||||
.find('[data-test="dashboard-component-tabs"] .ant-tabs-tab')
|
||||
.at(1) // will not call if it is already selected
|
||||
.simulate('click');
|
||||
|
||||
expect(onChangeTab.callCount).toBe(1);
|
||||
});
|
||||
|
||||
it('should not call onChangeTab when anchor link is clicked', () => {
|
||||
const onChangeTab = sinon.spy();
|
||||
const wrapper = setup({ editMode: true, onChangeTab });
|
||||
wrapper
|
||||
.find(
|
||||
'[data-test="dashboard-component-tabs"] .ant-tabs-tab [role="button"]',
|
||||
)
|
||||
.at(1) // will not call if it is already selected
|
||||
.simulate('click');
|
||||
|
||||
expect(onChangeTab.callCount).toBe(0);
|
||||
});
|
||||
|
||||
it('should render a HoverMenu in editMode', () => {
|
||||
let wrapper = setup();
|
||||
expect(wrapper.find(HoverMenu)).not.toExist();
|
||||
|
||||
wrapper = setup({ editMode: true });
|
||||
expect(wrapper.find(HoverMenu)).toExist();
|
||||
});
|
||||
|
||||
it('should render a DeleteComponentButton in editMode', () => {
|
||||
let wrapper = setup();
|
||||
expect(wrapper.find(DeleteComponentButton)).not.toExist();
|
||||
|
||||
wrapper = setup({ editMode: true });
|
||||
expect(wrapper.find(DeleteComponentButton)).toExist();
|
||||
});
|
||||
|
||||
it('should call deleteComponent when deleted', () => {
|
||||
const deleteComponent = sinon.spy();
|
||||
const wrapper = setup({ editMode: true, deleteComponent });
|
||||
wrapper.find(DeleteComponentButton).simulate('click');
|
||||
|
||||
expect(deleteComponent.callCount).toBe(1);
|
||||
});
|
||||
|
||||
it('should direct display direct-link tab', () => {
|
||||
let wrapper = shallow(<Tabs {...props} />);
|
||||
// default show first tab child
|
||||
expect(wrapper.state('tabIndex')).toBe(0);
|
||||
|
||||
// display child in directPathToChild list
|
||||
const directPathToChild =
|
||||
dashboardLayoutWithTabs.present.ROW_ID2.parents.slice();
|
||||
const directLinkProps = {
|
||||
...props,
|
||||
directPathToChild,
|
||||
};
|
||||
|
||||
wrapper = shallow(<Tabs {...directLinkProps} />);
|
||||
expect(wrapper.state('tabIndex')).toBe(1);
|
||||
});
|
||||
|
||||
it('should render Modal when clicked remove tab button', () => {
|
||||
const deleteComponent = sinon.spy();
|
||||
const modalMock = jest.spyOn(Modal, 'confirm');
|
||||
const wrapper = setup({ editMode: true, deleteComponent });
|
||||
wrapper.find('.ant-tabs-tab-remove').at(0).simulate('click');
|
||||
expect(modalMock.mock.calls).toHaveLength(1);
|
||||
expect(deleteComponent.callCount).toBe(0);
|
||||
});
|
||||
});
|
||||
@@ -1,84 +0,0 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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 React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import { DndProvider } from 'react-dnd';
|
||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||
|
||||
import DragDroppable from 'src/dashboard/components/dnd/DragDroppable';
|
||||
import DraggableNewComponent from 'src/dashboard/components/gridComponents/new/DraggableNewComponent';
|
||||
import { NEW_COMPONENTS_SOURCE_ID } from 'src/dashboard/util/constants';
|
||||
import {
|
||||
NEW_COMPONENT_SOURCE_TYPE,
|
||||
CHART_TYPE,
|
||||
} from 'src/dashboard/util/componentTypes';
|
||||
|
||||
describe('DraggableNewComponent', () => {
|
||||
const props = {
|
||||
id: 'id',
|
||||
type: CHART_TYPE,
|
||||
label: 'label!',
|
||||
className: 'a_class',
|
||||
};
|
||||
|
||||
function setup(overrideProps) {
|
||||
// We have to wrap provide DragDropContext for the underlying DragDroppable
|
||||
// otherwise we cannot assert on DragDroppable children
|
||||
const wrapper = mount(
|
||||
<DndProvider backend={HTML5Backend}>
|
||||
<DraggableNewComponent {...props} {...overrideProps} />
|
||||
</DndProvider>,
|
||||
);
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
it('should render a DragDroppable', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find(DragDroppable)).toExist();
|
||||
});
|
||||
|
||||
it('should pass component={ type, id } to DragDroppable', () => {
|
||||
const wrapper = setup();
|
||||
const dragdroppable = wrapper.find(DragDroppable);
|
||||
expect(dragdroppable.prop('component')).toEqual({
|
||||
id: props.id,
|
||||
type: props.type,
|
||||
});
|
||||
});
|
||||
|
||||
it('should pass appropriate parent source and id to DragDroppable', () => {
|
||||
const wrapper = setup();
|
||||
const dragdroppable = wrapper.find(DragDroppable);
|
||||
expect(dragdroppable.prop('parentComponent')).toEqual({
|
||||
id: NEW_COMPONENTS_SOURCE_ID,
|
||||
type: NEW_COMPONENT_SOURCE_TYPE,
|
||||
});
|
||||
});
|
||||
|
||||
it('should render the passed label', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find('.new-component').text()).toBe(props.label);
|
||||
});
|
||||
|
||||
it('should add the passed className', () => {
|
||||
const wrapper = setup();
|
||||
const className = `.new-component-placeholder.${props.className}`;
|
||||
expect(wrapper.find(className)).toExist();
|
||||
});
|
||||
});
|
||||
@@ -1,45 +0,0 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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 React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import DraggableNewComponent from 'src/dashboard/components/gridComponents/new/DraggableNewComponent';
|
||||
import NewColumn from 'src/dashboard/components/gridComponents/new/NewColumn';
|
||||
|
||||
import { NEW_COLUMN_ID } from 'src/dashboard/util/constants';
|
||||
import { COLUMN_TYPE } from 'src/dashboard/util/componentTypes';
|
||||
|
||||
describe('NewColumn', () => {
|
||||
function setup() {
|
||||
return shallow(<NewColumn />);
|
||||
}
|
||||
|
||||
it('should render a DraggableNewComponent', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find(DraggableNewComponent)).toExist();
|
||||
});
|
||||
|
||||
it('should set appropriate type and id', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find(DraggableNewComponent).props()).toMatchObject({
|
||||
type: COLUMN_TYPE,
|
||||
id: NEW_COLUMN_ID,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,45 +0,0 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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 React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import DraggableNewComponent from 'src/dashboard/components/gridComponents/new/DraggableNewComponent';
|
||||
import NewDivider from 'src/dashboard/components/gridComponents/new/NewDivider';
|
||||
|
||||
import { NEW_DIVIDER_ID } from 'src/dashboard/util/constants';
|
||||
import { DIVIDER_TYPE } from 'src/dashboard/util/componentTypes';
|
||||
|
||||
describe('NewDivider', () => {
|
||||
function setup() {
|
||||
return shallow(<NewDivider />);
|
||||
}
|
||||
|
||||
it('should render a DraggableNewComponent', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find(DraggableNewComponent)).toExist();
|
||||
});
|
||||
|
||||
it('should set appropriate type and id', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find(DraggableNewComponent).props()).toMatchObject({
|
||||
type: DIVIDER_TYPE,
|
||||
id: NEW_DIVIDER_ID,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,45 +0,0 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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 React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import DraggableNewComponent from 'src/dashboard/components/gridComponents/new/DraggableNewComponent';
|
||||
import NewHeader from 'src/dashboard/components/gridComponents/new/NewHeader';
|
||||
|
||||
import { NEW_HEADER_ID } from 'src/dashboard/util/constants';
|
||||
import { HEADER_TYPE } from 'src/dashboard/util/componentTypes';
|
||||
|
||||
describe('NewHeader', () => {
|
||||
function setup() {
|
||||
return shallow(<NewHeader />);
|
||||
}
|
||||
|
||||
it('should render a DraggableNewComponent', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find(DraggableNewComponent)).toExist();
|
||||
});
|
||||
|
||||
it('should set appropriate type and id', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find(DraggableNewComponent).props()).toMatchObject({
|
||||
type: HEADER_TYPE,
|
||||
id: NEW_HEADER_ID,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,45 +0,0 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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 React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import DraggableNewComponent from 'src/dashboard/components/gridComponents/new/DraggableNewComponent';
|
||||
import NewRow from 'src/dashboard/components/gridComponents/new/NewRow';
|
||||
|
||||
import { NEW_ROW_ID } from 'src/dashboard/util/constants';
|
||||
import { ROW_TYPE } from 'src/dashboard/util/componentTypes';
|
||||
|
||||
describe('NewRow', () => {
|
||||
function setup() {
|
||||
return shallow(<NewRow />);
|
||||
}
|
||||
|
||||
it('should render a DraggableNewComponent', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find(DraggableNewComponent)).toExist();
|
||||
});
|
||||
|
||||
it('should set appropriate type and id', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find(DraggableNewComponent).props()).toMatchObject({
|
||||
type: ROW_TYPE,
|
||||
id: NEW_ROW_ID,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,45 +0,0 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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 React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import DraggableNewComponent from 'src/dashboard/components/gridComponents/new/DraggableNewComponent';
|
||||
import NewTabs from 'src/dashboard/components/gridComponents/new/NewTabs';
|
||||
|
||||
import { NEW_TABS_ID } from 'src/dashboard/util/constants';
|
||||
import { TABS_TYPE } from 'src/dashboard/util/componentTypes';
|
||||
|
||||
describe('NewTabs', () => {
|
||||
function setup() {
|
||||
return shallow(<NewTabs />);
|
||||
}
|
||||
|
||||
it('should render a DraggableNewComponent', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find(DraggableNewComponent)).toExist();
|
||||
});
|
||||
|
||||
it('should set appropriate type and id', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find(DraggableNewComponent).props()).toMatchObject({
|
||||
type: TABS_TYPE,
|
||||
id: NEW_TABS_ID,
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user