mirror of https://github.com/apache/superset.git
chore: Upgrade Webpack to v5 (#16701)
* Upgrade Webpack to v5 * Remove mapbox hack * Replace url-loaders and file-loaders with asset modules * Remove 1 rule * Change --colors to --color * Remove invalid option (--no-progress) * Remove url-loader, bump plugin * Fix AnnotationLayer formula check * Remove redundant tests * Bump cypress packages * Remove old comment * Fix tests * Remove checks for number of scripts in markdown test * Cosmetic changes * Add tests * Fix test * Fix test * Fixes test warnings * disable flaky test Co-authored-by: Ville Brofeldt <ville.v.brofeldt@gmail.com> Co-authored-by: Michael S. Molina <michael.s.molina@gmail.com>
This commit is contained in:
parent
9b17e86b44
commit
486e0d412c
|
@ -70,7 +70,7 @@ build-assets() {
|
|||
cd "$GITHUB_WORKSPACE/superset-frontend"
|
||||
|
||||
say "::group::Build static assets"
|
||||
npm run build -- --no-progress
|
||||
npm run build
|
||||
say "::endgroup::"
|
||||
}
|
||||
|
||||
|
@ -82,7 +82,7 @@ build-instrumented-assets() {
|
|||
if [[ -f "$ASSETS_MANIFEST" ]]; then
|
||||
echo 'Skip frontend build because instrumented static assets already exist.'
|
||||
else
|
||||
npm run build-instrumented -- --no-progress
|
||||
npm run build-instrumented
|
||||
cache-save instrumented-assets
|
||||
fi
|
||||
say "::endgroup::"
|
||||
|
|
|
@ -24,11 +24,7 @@ describe('Dashboard edit markdown', () => {
|
|||
cy.visit(TABBED_DASHBOARD);
|
||||
});
|
||||
|
||||
it('should load AceEditor on demand', () => {
|
||||
let numScripts = 0;
|
||||
cy.get('script').then(nodes => {
|
||||
numScripts = nodes.length;
|
||||
});
|
||||
it('should add markdown component to dashboard', () => {
|
||||
cy.get('[data-test="dashboard-header"]')
|
||||
.find('[aria-label="edit-alt"]')
|
||||
.click();
|
||||
|
@ -37,11 +33,6 @@ describe('Dashboard edit markdown', () => {
|
|||
cy.get('[data-test="dashboard-header"]')
|
||||
.find('[aria-label="more-horiz"]')
|
||||
.click();
|
||||
cy.get('script').then(nodes => {
|
||||
// load 5 new script chunks for css editor
|
||||
expect(nodes.length).to.greaterThan(numScripts);
|
||||
numScripts = nodes.length;
|
||||
});
|
||||
cy.get('[data-test="grid-row-background--transparent"]')
|
||||
.first()
|
||||
.as('component-background-first');
|
||||
|
@ -49,11 +40,6 @@ describe('Dashboard edit markdown', () => {
|
|||
drag('[data-test="new-component"]', 'Markdown').to(
|
||||
'@component-background-first',
|
||||
);
|
||||
cy.get('script').then(nodes => {
|
||||
// load more scripts for markdown editor
|
||||
expect(nodes.length).to.greaterThan(numScripts);
|
||||
numScripts = nodes.length;
|
||||
});
|
||||
cy.get('[data-test="dashboard-markdown-editor"]')
|
||||
.should(
|
||||
'have.text',
|
||||
|
@ -75,12 +61,6 @@ describe('Dashboard edit markdown', () => {
|
|||
|
||||
cy.get('[data-test="dashboard-markdown-editor"]').contains('Test resize');
|
||||
|
||||
// entering edit mode does not add new scripts
|
||||
// (though scripts may still be removed by others)
|
||||
cy.get('script').then(nodes => {
|
||||
expect(nodes.length).to.greaterThan(numScripts);
|
||||
});
|
||||
|
||||
cy.get('@component-background-first').click('right');
|
||||
cy.get('[data-test="dashboard-component-chart-holder"]')
|
||||
.find('.ace_content')
|
||||
|
|
|
@ -26,10 +26,6 @@ describe('AdhocFilters', () => {
|
|||
cy.verifySliceSuccess({ waitAlias: '@postJson' });
|
||||
});
|
||||
|
||||
xit('Should not load mathjs when not needed', () => {
|
||||
cy.get('script[src*="mathjs"]').should('have.length', 0);
|
||||
});
|
||||
|
||||
let numScripts = 0;
|
||||
|
||||
xit('Should load AceEditor scripts when needed', () => {
|
||||
|
|
|
@ -105,9 +105,6 @@ describe('VizType control', () => {
|
|||
cy.get('[role="button"]').contains('Line Chart').click();
|
||||
cy.get('button').contains('Select').click();
|
||||
|
||||
// should load mathjs for line chart
|
||||
cy.get('script[src*="mathjs"]').should('have.length', 1);
|
||||
|
||||
cy.get('button[data-test="run-query-button"]').click();
|
||||
cy.verifySliceSuccess({
|
||||
waitAlias: '@lineChartData',
|
||||
|
|
|
@ -32,14 +32,6 @@ describe('Visualization > Line', () => {
|
|||
cy.get('.ant-alert-warning').contains(`Metrics: cannot be empty`);
|
||||
});
|
||||
|
||||
it('should preload mathjs', () => {
|
||||
cy.get('script[src*="mathjs"]').should('have.length', 1);
|
||||
cy.contains('Add annotation layer').scrollIntoView().click();
|
||||
// should not load additional mathjs
|
||||
cy.get('script[src*="mathjs"]').should('have.length', 1);
|
||||
cy.contains('Layer configuration');
|
||||
});
|
||||
|
||||
it('should not show validator error when metric added', () => {
|
||||
const formData = { ...LINE_CHART_DEFAULTS, metrics: [] };
|
||||
cy.visitChartByParams(JSON.stringify(formData));
|
||||
|
@ -85,7 +77,6 @@ describe('Visualization > Line', () => {
|
|||
const formData = { ...LINE_CHART_DEFAULTS, metrics: [NUM_METRIC] };
|
||||
cy.visitChartByParams(JSON.stringify(formData));
|
||||
cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
|
||||
cy.get('script[src*="mathjs"]').should('have.length', 1);
|
||||
});
|
||||
|
||||
it('should work with groupby', () => {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -7,18 +7,18 @@
|
|||
"cypress-run-chrome": "cypress run --browser chrome --headless",
|
||||
"cypress-debug": "cypress open --config watchForFileChanges=true"
|
||||
},
|
||||
"author": "Apcahe",
|
||||
"author": "Apache",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@cypress/code-coverage": "^3.9.2",
|
||||
"@superset-ui/core": "^0.17.42",
|
||||
"@cypress/code-coverage": "^3.9.11",
|
||||
"@superset-ui/core": "^0.18.4",
|
||||
"react-dom": "^16.13.0",
|
||||
"rison": "^0.1.1",
|
||||
"shortid": "^2.2.15"
|
||||
},
|
||||
"devDependencies": {
|
||||
"cypress": "^6.3.0",
|
||||
"eslint-plugin-cypress": "^2.11.2"
|
||||
"eslint-plugin-cypress": "^2.12.1"
|
||||
},
|
||||
"nyc": {
|
||||
"reporter": [
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -12,12 +12,12 @@
|
|||
"test": "cross-env NODE_ENV=test jest",
|
||||
"type": "tsc --noEmit",
|
||||
"cover": "cross-env NODE_ENV=test jest --coverage",
|
||||
"dev": "webpack --mode=development --colors --debug --watch",
|
||||
"dev": "webpack --mode=development --color --watch",
|
||||
"dev-server": "cross-env NODE_ENV=development BABEL_ENV=development node --max_old_space_size=4096 ./node_modules/webpack-dev-server/bin/webpack-dev-server.js --mode=development",
|
||||
"prod": "npm run build",
|
||||
"build-dev": "cross-env NODE_OPTIONS=--max_old_space_size=8192 NODE_ENV=development webpack --mode=development --colors",
|
||||
"build-instrumented": "cross-env NODE_ENV=development BABEL_ENV=instrumented webpack --mode=development --colors",
|
||||
"build": "cross-env NODE_OPTIONS=--max_old_space_size=8192 NODE_ENV=production BABEL_ENV=\"${BABEL_ENV:=production}\" webpack --mode=production --colors",
|
||||
"build-dev": "cross-env NODE_OPTIONS=--max_old_space_size=8192 NODE_ENV=development webpack --mode=development --color",
|
||||
"build-instrumented": "cross-env NODE_ENV=development BABEL_ENV=instrumented webpack --mode=development --color",
|
||||
"build": "cross-env NODE_OPTIONS=--max_old_space_size=8192 NODE_ENV=production BABEL_ENV=\"${BABEL_ENV:=production}\" webpack --mode=production --color",
|
||||
"lint": "eslint --ignore-path=.eslintignore --ext .js,.jsx,.ts,.tsx . && npm run type",
|
||||
"prettier-check": "prettier --check '{src,stylesheets}/**/*.{css,less,sass,scss}'",
|
||||
"lint-fix": "eslint --fix --ignore-path=.eslintignore --ext .js,.jsx,.ts,tsx . && npm run clean-css && npm run type",
|
||||
|
@ -117,7 +117,7 @@
|
|||
"fuse.js": "^6.4.6",
|
||||
"geolib": "^2.0.24",
|
||||
"global-box": "^1.2.0",
|
||||
"html-webpack-plugin": "^4.5.1",
|
||||
"html-webpack-plugin": "^5.3.2",
|
||||
"immer": "^9.0.6",
|
||||
"immutable": "^4.0.0-rc.12",
|
||||
"interweave": "^11.2.0",
|
||||
|
@ -129,7 +129,7 @@
|
|||
"lodash": "^4.17.21",
|
||||
"lodash-es": "^4.17.21",
|
||||
"match-sorter": "^6.1.0",
|
||||
"mathjs": "^8.0.1",
|
||||
"math-expression-evaluator": "^1.3.8",
|
||||
"memoize-one": "^5.1.1",
|
||||
"moment": "^2.26.0",
|
||||
"moment-timezone": "^0.5.33",
|
||||
|
@ -210,7 +210,7 @@
|
|||
"@storybook/client-api": "^6.1.17",
|
||||
"@storybook/preset-typescript": "^3.0.0",
|
||||
"@storybook/react": "^6.1.20",
|
||||
"@svgr/webpack": "^5.4.0",
|
||||
"@svgr/webpack": "^5.5.0",
|
||||
"@testing-library/dom": "^7.29.4",
|
||||
"@testing-library/jest-dom": "^5.11.6",
|
||||
"@testing-library/react": "^11.2.0",
|
||||
|
@ -252,20 +252,19 @@
|
|||
"babel-plugin-dynamic-import-node": "^2.3.3",
|
||||
"babel-plugin-jsx-remove-data-test-id": "^2.1.3",
|
||||
"babel-plugin-lodash": "^3.3.4",
|
||||
"cache-loader": "^1.2.2",
|
||||
"clean-webpack-plugin": "^3.0.0",
|
||||
"copy-webpack-plugin": "^6.0.3",
|
||||
"copy-webpack-plugin": "^9.0.1",
|
||||
"cross-env": "^5.2.0",
|
||||
"css-loader": "^1.0.0",
|
||||
"css-loader": "^6.2.0",
|
||||
"css-minimizer-webpack-plugin": "^3.0.2",
|
||||
"enzyme": "^3.10.0",
|
||||
"enzyme-adapter-react-16": "^1.14.0",
|
||||
"eslint": "^7.17.0",
|
||||
"eslint-config-airbnb": "^18.2.1",
|
||||
"eslint-config-prettier": "^7.1.0",
|
||||
"eslint-import-resolver-typescript": "^2.3.0",
|
||||
"eslint-import-resolver-webpack": "^0.13.0",
|
||||
"eslint-import-resolver-typescript": "^2.5.0",
|
||||
"eslint-import-resolver-webpack": "^0.13.1",
|
||||
"eslint-plugin-cypress": "^2.11.2",
|
||||
"eslint-plugin-import": "^2.22.1",
|
||||
"eslint-plugin-import": "^2.24.2",
|
||||
"eslint-plugin-jest": "^24.1.3",
|
||||
"eslint-plugin-jest-dom": "^3.6.5",
|
||||
"eslint-plugin-jsx-a11y": "^6.4.1",
|
||||
|
@ -277,9 +276,9 @@
|
|||
"exports-loader": "^0.7.0",
|
||||
"fetch-mock": "^7.7.3",
|
||||
"file-loader": "^6.0.0",
|
||||
"fork-ts-checker-webpack-plugin": "^0.4.9",
|
||||
"fork-ts-checker-webpack-plugin": "^6.3.3",
|
||||
"ignore-styles": "^5.0.1",
|
||||
"imports-loader": "^0.7.1",
|
||||
"imports-loader": "^3.0.0",
|
||||
"jest": "^26.6.3",
|
||||
"jest-environment-enzyme": "^7.1.2",
|
||||
"jest-enzyme": "^7.1.2",
|
||||
|
@ -287,32 +286,31 @@
|
|||
"jsdom": "^16.4.0",
|
||||
"less": "^3.12.2",
|
||||
"less-loader": "^5.0.0",
|
||||
"mini-css-extract-plugin": "^0.4.0",
|
||||
"mini-css-extract-plugin": "^2.3.0",
|
||||
"mock-socket": "^9.0.3",
|
||||
"node-fetch": "^2.6.1",
|
||||
"optimize-css-assets-webpack-plugin": "^5.0.1",
|
||||
"po2json": "^0.4.5",
|
||||
"prettier": "^2.2.1",
|
||||
"process": "^0.11.10",
|
||||
"react-test-renderer": "^16.9.0",
|
||||
"redux-mock-store": "^1.5.4",
|
||||
"sinon": "^9.0.2",
|
||||
"source-map-support": "^0.5.16",
|
||||
"speed-measure-webpack-plugin": "^1.2.3",
|
||||
"speed-measure-webpack-plugin": "^1.5.0",
|
||||
"storybook-addon-jsx": "^7.3.3",
|
||||
"storybook-addon-paddings": "^3.2.0",
|
||||
"style-loader": "^1.0.0",
|
||||
"thread-loader": "^1.2.0",
|
||||
"transform-loader": "^0.2.3",
|
||||
"style-loader": "^3.2.1",
|
||||
"thread-loader": "^3.0.4",
|
||||
"transform-loader": "^0.2.4",
|
||||
"ts-jest": "^26.4.2",
|
||||
"ts-loader": "^8.0.7",
|
||||
"ts-loader": "^9.2.5",
|
||||
"typescript": "^4.1.6",
|
||||
"url-loader": "^1.0.1",
|
||||
"webpack": "^4.42.0",
|
||||
"webpack-bundle-analyzer": "^3.6.1",
|
||||
"webpack-cli": "^3.3.11",
|
||||
"webpack-dev-server": "^3.11.0",
|
||||
"webpack-manifest-plugin": "^2.2.0",
|
||||
"webpack-sources": "^1.4.3",
|
||||
"webpack": "^5.52.1",
|
||||
"webpack-bundle-analyzer": "^4.4.2",
|
||||
"webpack-cli": "^4.8.0",
|
||||
"webpack-dev-server": "^4.2.0",
|
||||
"webpack-manifest-plugin": "^4.0.2",
|
||||
"webpack-sources": "^3.2.0",
|
||||
"yargs": "^15.4.1"
|
||||
},
|
||||
"stylelint": {
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import AntdIcon from '@ant-design/icons';
|
||||
import { styled } from '@superset-ui/core';
|
||||
import { ReactComponent as TransparentIcon } from 'images/icons/transparent.svg';
|
||||
import TransparentIcon from 'images/icons/transparent.svg';
|
||||
import IconType from './IconType';
|
||||
|
||||
const AntdIconComponent = ({
|
||||
|
|
|
@ -20,7 +20,7 @@ import { t, styled } from '@superset-ui/core';
|
|||
import React, { useEffect } from 'react';
|
||||
import { Empty } from 'src/common/components';
|
||||
import Alert from 'src/components/Alert';
|
||||
import { ReactComponent as EmptyImage } from 'images/empty.svg';
|
||||
import EmptyImage from 'images/empty.svg';
|
||||
import cx from 'classnames';
|
||||
import Button from 'src/components/Button';
|
||||
import Icons from 'src/components/Icons';
|
||||
|
|
|
@ -20,7 +20,7 @@ import React from 'react';
|
|||
import PropTypes from 'prop-types';
|
||||
import { CompactPicker } from 'react-color';
|
||||
import Button from 'src/components/Button';
|
||||
import { parse as mathjsParse } from 'mathjs';
|
||||
import mexp from 'math-expression-evaluator';
|
||||
import {
|
||||
t,
|
||||
SupersetClient,
|
||||
|
@ -204,16 +204,31 @@ export default class AnnotationLayer extends React.PureComponent {
|
|||
return sources;
|
||||
}
|
||||
|
||||
isValidFormula(value, annotationType) {
|
||||
isValidFormula(expression, annotationType) {
|
||||
if (annotationType === ANNOTATION_TYPES.FORMULA) {
|
||||
try {
|
||||
mathjsParse(value).compile().evaluate({ x: 0 });
|
||||
} catch (err) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
const token = {
|
||||
type: 3,
|
||||
token: 'x',
|
||||
show: 'x',
|
||||
value: 'x',
|
||||
};
|
||||
|
||||
// handle input like "y = x+1" instead of just "x+1"
|
||||
const subexpressions = expression.split('=');
|
||||
if (
|
||||
subexpressions.length > 2 ||
|
||||
(subexpressions[1] && !subexpressions[0].match(/^\s*[a-zA-Z]\w*\s*$/))
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
mexp.eval(subexpressions[1] ?? subexpressions[0], [token], { x: 0 });
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
isValidForm() {
|
||||
const {
|
||||
|
@ -238,7 +253,7 @@ export default class AnnotationLayer extends React.PureComponent {
|
|||
errors.push(validateNonEmpty(intervalEndColumn));
|
||||
}
|
||||
}
|
||||
errors.push(this.isValidFormula(value, annotationType));
|
||||
errors.push(!this.isValidFormula(value, annotationType));
|
||||
return !errors.filter(x => x).length;
|
||||
}
|
||||
|
||||
|
@ -442,7 +457,7 @@ export default class AnnotationLayer extends React.PureComponent {
|
|||
value={value}
|
||||
onChange={this.handleValue}
|
||||
validationErrors={
|
||||
this.isValidFormula(value, annotationType) ? ['Bad formula.'] : []
|
||||
!this.isValidFormula(value, annotationType) ? ['Bad formula.'] : []
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -57,16 +57,18 @@ beforeAll(() => {
|
|||
);
|
||||
});
|
||||
|
||||
test('renders with default props', () => {
|
||||
const { container } = render(<AnnotationLayer {...defaultProps} />);
|
||||
expect(container).toBeInTheDocument();
|
||||
const waitForRender = (props?: any) =>
|
||||
waitFor(() => render(<AnnotationLayer {...defaultProps} {...props} />));
|
||||
|
||||
test('renders with default props', async () => {
|
||||
await waitForRender();
|
||||
expect(screen.getByRole('button', { name: 'Apply' })).toBeDisabled();
|
||||
expect(screen.getByRole('button', { name: 'OK' })).toBeDisabled();
|
||||
expect(screen.getByRole('button', { name: 'Cancel' })).toBeEnabled();
|
||||
});
|
||||
|
||||
test('renders extra checkboxes when type is time series', () => {
|
||||
render(<AnnotationLayer {...defaultProps} />);
|
||||
test('renders extra checkboxes when type is time series', async () => {
|
||||
await waitForRender();
|
||||
expect(
|
||||
screen.queryByRole('button', { name: 'Show Markers' }),
|
||||
).not.toBeInTheDocument();
|
||||
|
@ -82,7 +84,7 @@ test('renders extra checkboxes when type is time series', () => {
|
|||
});
|
||||
|
||||
test('enables apply and ok buttons', async () => {
|
||||
render(<AnnotationLayer {...defaultProps} />);
|
||||
await waitForRender();
|
||||
userEvent.type(screen.getByLabelText('Name'), 'Test');
|
||||
userEvent.type(screen.getByLabelText('Formula'), '2x');
|
||||
await waitFor(() => {
|
||||
|
@ -91,68 +93,47 @@ test('enables apply and ok buttons', async () => {
|
|||
});
|
||||
});
|
||||
|
||||
test('triggers addAnnotationLayer when apply button is clicked', () => {
|
||||
test('triggers addAnnotationLayer when apply button is clicked', async () => {
|
||||
const addAnnotationLayer = jest.fn();
|
||||
render(
|
||||
<AnnotationLayer
|
||||
{...defaultProps}
|
||||
name="Test"
|
||||
value="2x"
|
||||
addAnnotationLayer={addAnnotationLayer}
|
||||
/>,
|
||||
);
|
||||
await waitForRender({ name: 'Test', value: '2x', addAnnotationLayer });
|
||||
userEvent.click(screen.getByRole('button', { name: 'Apply' }));
|
||||
expect(addAnnotationLayer).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('triggers addAnnotationLayer and close when ok button is clicked', () => {
|
||||
test('triggers addAnnotationLayer and close when ok button is clicked', async () => {
|
||||
const addAnnotationLayer = jest.fn();
|
||||
const close = jest.fn();
|
||||
render(
|
||||
<AnnotationLayer
|
||||
{...defaultProps}
|
||||
name="Test"
|
||||
value="2x"
|
||||
addAnnotationLayer={addAnnotationLayer}
|
||||
close={close}
|
||||
/>,
|
||||
);
|
||||
await waitForRender({ name: 'Test', value: '2x', addAnnotationLayer, close });
|
||||
userEvent.click(screen.getByRole('button', { name: 'OK' }));
|
||||
expect(addAnnotationLayer).toHaveBeenCalled();
|
||||
expect(close).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('triggers close when cancel button is clicked', () => {
|
||||
test('triggers close when cancel button is clicked', async () => {
|
||||
const close = jest.fn();
|
||||
render(<AnnotationLayer {...defaultProps} close={close} />);
|
||||
await waitForRender({ close });
|
||||
userEvent.click(screen.getByRole('button', { name: 'Cancel' }));
|
||||
expect(close).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('triggers removeAnnotationLayer and close when remove button is clicked', () => {
|
||||
test('triggers removeAnnotationLayer and close when remove button is clicked', async () => {
|
||||
const removeAnnotationLayer = jest.fn();
|
||||
const close = jest.fn();
|
||||
render(
|
||||
<AnnotationLayer
|
||||
{...defaultProps}
|
||||
name="Test"
|
||||
value="2x"
|
||||
removeAnnotationLayer={removeAnnotationLayer}
|
||||
close={close}
|
||||
/>,
|
||||
);
|
||||
await waitForRender({
|
||||
name: 'Test',
|
||||
value: '2x',
|
||||
removeAnnotationLayer,
|
||||
close,
|
||||
});
|
||||
userEvent.click(screen.getByRole('button', { name: 'Remove' }));
|
||||
expect(removeAnnotationLayer).toHaveBeenCalled();
|
||||
expect(close).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('renders chart options', async () => {
|
||||
render(
|
||||
<AnnotationLayer
|
||||
{...defaultProps}
|
||||
annotationType={ANNOTATION_TYPES_METADATA.EVENT.value}
|
||||
/>,
|
||||
);
|
||||
await waitForRender({
|
||||
annotationType: ANNOTATION_TYPES_METADATA.EVENT.value,
|
||||
});
|
||||
userEvent.click(screen.getByText('2 option(s)'));
|
||||
userEvent.click(screen.getByText('Superset annotation'));
|
||||
expect(await screen.findByLabelText('Annotation layer')).toBeInTheDocument();
|
||||
|
@ -162,15 +143,12 @@ test('renders chart options', async () => {
|
|||
});
|
||||
|
||||
test('keeps apply disabled when missing required fields', async () => {
|
||||
render(
|
||||
<AnnotationLayer
|
||||
{...defaultProps}
|
||||
annotationType={ANNOTATION_TYPES_METADATA.EVENT.value}
|
||||
sourceType="Table"
|
||||
/>,
|
||||
);
|
||||
userEvent.click(await screen.findByText('1 option(s)'));
|
||||
userEvent.click(screen.getByText('Chart A'));
|
||||
await waitForRender({
|
||||
annotationType: ANNOTATION_TYPES_METADATA.EVENT.value,
|
||||
sourceType: 'Table',
|
||||
});
|
||||
userEvent.click(screen.getByText('1 option(s)'));
|
||||
await waitFor(() => userEvent.click(screen.getByText('Chart A')));
|
||||
expect(
|
||||
screen.getByText('Annotation Slice Configuration'),
|
||||
).toBeInTheDocument();
|
||||
|
@ -188,3 +166,48 @@ test('keeps apply disabled when missing required fields', async () => {
|
|||
|
||||
expect(screen.getByRole('button', { name: 'Apply' })).toBeDisabled();
|
||||
});
|
||||
|
||||
test.skip('Disable apply button if formula is incorrect', async () => {
|
||||
// TODO: fix flaky test that passes locally but fails on CI
|
||||
await waitForRender({ name: 'test' });
|
||||
|
||||
userEvent.clear(screen.getByLabelText('Formula'));
|
||||
userEvent.type(screen.getByLabelText('Formula'), 'x+1');
|
||||
await waitFor(() => {
|
||||
expect(screen.getByLabelText('Formula')).toHaveValue('x+1');
|
||||
expect(screen.getByRole('button', { name: 'OK' })).toBeEnabled();
|
||||
expect(screen.getByRole('button', { name: 'Apply' })).toBeEnabled();
|
||||
});
|
||||
|
||||
userEvent.clear(screen.getByLabelText('Formula'));
|
||||
userEvent.type(screen.getByLabelText('Formula'), 'y = x*2+1');
|
||||
await waitFor(() => {
|
||||
expect(screen.getByLabelText('Formula')).toHaveValue('y = x*2+1');
|
||||
expect(screen.getByRole('button', { name: 'OK' })).toBeEnabled();
|
||||
expect(screen.getByRole('button', { name: 'Apply' })).toBeEnabled();
|
||||
});
|
||||
|
||||
userEvent.clear(screen.getByLabelText('Formula'));
|
||||
userEvent.type(screen.getByLabelText('Formula'), 'y+1');
|
||||
await waitFor(() => {
|
||||
expect(screen.getByLabelText('Formula')).toHaveValue('y+1');
|
||||
expect(screen.getByRole('button', { name: 'OK' })).toBeDisabled();
|
||||
expect(screen.getByRole('button', { name: 'Apply' })).toBeDisabled();
|
||||
});
|
||||
|
||||
userEvent.clear(screen.getByLabelText('Formula'));
|
||||
userEvent.type(screen.getByLabelText('Formula'), 'x+');
|
||||
await waitFor(() => {
|
||||
expect(screen.getByLabelText('Formula')).toHaveValue('x+');
|
||||
expect(screen.getByRole('button', { name: 'OK' })).toBeDisabled();
|
||||
expect(screen.getByRole('button', { name: 'Apply' })).toBeDisabled();
|
||||
});
|
||||
|
||||
userEvent.clear(screen.getByLabelText('Formula'));
|
||||
userEvent.type(screen.getByLabelText('Formula'), 'y = z+1');
|
||||
await waitFor(() => {
|
||||
expect(screen.getByLabelText('Formula')).toHaveValue('y = z+1');
|
||||
expect(screen.getByRole('button', { name: 'OK' })).toBeDisabled();
|
||||
expect(screen.getByRole('button', { name: 'Apply' })).toBeDisabled();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -37,7 +37,7 @@ export default class FilterBoxChartPlugin extends ChartPlugin {
|
|||
controlPanel,
|
||||
metadata,
|
||||
transformProps,
|
||||
loadChart: () => import('./FilterBox.jsx'),
|
||||
loadChart: () => import('./FilterBox'),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ export default class TimeTableChartPlugin extends ChartPlugin {
|
|||
super({
|
||||
metadata,
|
||||
transformProps,
|
||||
loadChart: () => import('./TimeTable.jsx'),
|
||||
loadChart: () => import('./TimeTable'),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,17 +21,19 @@ const fs = require('fs');
|
|||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
|
||||
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
|
||||
const CopyPlugin = require('copy-webpack-plugin');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
|
||||
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
|
||||
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin');
|
||||
const ManifestPlugin = require('webpack-manifest-plugin');
|
||||
const {
|
||||
WebpackManifestPlugin,
|
||||
getCompilerHooks,
|
||||
} = require('webpack-manifest-plugin');
|
||||
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
|
||||
const parsedArgs = require('yargs').argv;
|
||||
const getProxyConfig = require('./webpack.proxy-config');
|
||||
const packageConfig = require('./package.json');
|
||||
const packageConfig = require('./package');
|
||||
|
||||
// input dir
|
||||
const APP_DIR = path.resolve(__dirname, './');
|
||||
|
@ -56,8 +58,8 @@ const output = {
|
|||
publicPath: `${ASSET_BASE_URL}/static/assets/`,
|
||||
};
|
||||
if (isDevMode) {
|
||||
output.filename = '[name].[hash:8].entry.js';
|
||||
output.chunkFilename = '[name].[hash:8].chunk.js';
|
||||
output.filename = '[name].[contenthash:8].entry.js';
|
||||
output.chunkFilename = '[name].[contenthash:8].chunk.js';
|
||||
} else if (nameChunks) {
|
||||
output.filename = '[name].[chunkhash].entry.js';
|
||||
output.chunkFilename = '[name].[chunkhash].chunk.js';
|
||||
|
@ -66,9 +68,17 @@ if (isDevMode) {
|
|||
output.chunkFilename = '[chunkhash].chunk.js';
|
||||
}
|
||||
|
||||
if (!isDevMode) {
|
||||
output.clean = true;
|
||||
}
|
||||
|
||||
const plugins = [
|
||||
new webpack.ProvidePlugin({
|
||||
process: 'process/browser',
|
||||
}),
|
||||
|
||||
// creates a manifest.json mapping of name to hashed output used in template files
|
||||
new ManifestPlugin({
|
||||
new WebpackManifestPlugin({
|
||||
publicPath: output.publicPath,
|
||||
seed: { app: 'superset' },
|
||||
// This enables us to include all relevant files for an entry
|
||||
|
@ -109,9 +119,10 @@ const plugins = [
|
|||
|
||||
// runs type checking on a separate process to speed up the build
|
||||
new ForkTsCheckerWebpackPlugin({
|
||||
eslint: true,
|
||||
checkSyntacticErrors: true,
|
||||
eslint: {
|
||||
files: './src/**/*.{ts,tsx,js,jsx}',
|
||||
memoryLimit: 4096,
|
||||
},
|
||||
}),
|
||||
|
||||
new CopyPlugin({
|
||||
|
@ -141,17 +152,6 @@ if (!process.env.CI) {
|
|||
plugins.push(new webpack.ProgressPlugin());
|
||||
}
|
||||
|
||||
// clean up built assets if not from dev-server
|
||||
if (!isDevServer) {
|
||||
plugins.push(
|
||||
new CleanWebpackPlugin({
|
||||
dry: false,
|
||||
// required because the build directory is outside the frontend directory:
|
||||
dangerouslyAllowCleanPatternsOutsideProject: true,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
if (!isDevMode) {
|
||||
// text loading (webpack 4+)
|
||||
plugins.push(
|
||||
|
@ -160,7 +160,6 @@ if (!isDevMode) {
|
|||
chunkFilename: '[name].[chunkhash].chunk.css',
|
||||
}),
|
||||
);
|
||||
plugins.push(new OptimizeCSSAssetsPlugin());
|
||||
}
|
||||
|
||||
const PREAMBLE = [path.join(APP_DIR, '/src/preamble.ts')];
|
||||
|
@ -199,9 +198,6 @@ const babelLoader = {
|
|||
};
|
||||
|
||||
const config = {
|
||||
node: {
|
||||
fs: 'empty',
|
||||
},
|
||||
entry: {
|
||||
preamble: PREAMBLE,
|
||||
theme: path.join(APP_DIR, '/src/theme.ts'),
|
||||
|
@ -268,13 +264,6 @@ const config = {
|
|||
].join('|')})/`,
|
||||
),
|
||||
},
|
||||
// bundle large libraries separately
|
||||
mathjs: {
|
||||
name: 'mathjs',
|
||||
test: /\/node_modules\/mathjs\//,
|
||||
priority: 30,
|
||||
enforce: true,
|
||||
},
|
||||
// viz thumbnails are used in `addSlice` and `explore` page
|
||||
thumbnail: {
|
||||
name: 'thumbnail',
|
||||
|
@ -284,6 +273,7 @@ const config = {
|
|||
},
|
||||
},
|
||||
},
|
||||
minimizer: [new CssMinimizerPlugin()],
|
||||
},
|
||||
resolve: {
|
||||
modules: [APP_DIR, 'node_modules', ROOT_DIR],
|
||||
|
@ -302,16 +292,21 @@ const config = {
|
|||
},
|
||||
extensions: ['.ts', '.tsx', '.js', '.jsx', '.yml'],
|
||||
symlinks: false,
|
||||
fallback: {
|
||||
fs: false,
|
||||
vm: false,
|
||||
path: false,
|
||||
},
|
||||
},
|
||||
context: APP_DIR, // to automatically find tsconfig.json
|
||||
module: {
|
||||
// Uglifying mapbox-gl results in undefined errors, see
|
||||
// https://github.com/mapbox/mapbox-gl-js/issues/4359#issuecomment-288001933
|
||||
noParse: /(mapbox-gl)\.js$/,
|
||||
rules: [
|
||||
{
|
||||
test: /datatables\.net.*/,
|
||||
loader: 'imports-loader?define=>false',
|
||||
loader: 'imports-loader',
|
||||
options: {
|
||||
additionalCode: 'var define = false;',
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.tsx?$/,
|
||||
|
@ -388,52 +383,34 @@ const config = {
|
|||
{
|
||||
test: /\.png$/,
|
||||
issuer: {
|
||||
exclude: /\/src\/assets\/staticPages\//,
|
||||
not: [/\/src\/assets\/staticPages\//],
|
||||
},
|
||||
loader: 'url-loader',
|
||||
options: {
|
||||
limit: 10000,
|
||||
name: '[name].[hash:8].[ext]',
|
||||
type: 'asset',
|
||||
generator: {
|
||||
filename: '[name].[contenthash:8].[ext]',
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.png$/,
|
||||
issuer: {
|
||||
test: /\/src\/assets\/staticPages\//,
|
||||
},
|
||||
loader: 'url-loader',
|
||||
options: {
|
||||
limit: 150000, // Convert images < 150kb to base64 strings
|
||||
},
|
||||
issuer: /\/src\/assets\/staticPages\//,
|
||||
type: 'asset',
|
||||
},
|
||||
{
|
||||
test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
|
||||
issuer: {
|
||||
test: /\.(j|t)sx?$/,
|
||||
},
|
||||
issuer: /\.([jt])sx?$/,
|
||||
use: ['@svgr/webpack'],
|
||||
},
|
||||
{
|
||||
test: /\.(jpg|gif)$/,
|
||||
loader: 'file-loader',
|
||||
options: {
|
||||
name: '[name].[hash:8].[ext]',
|
||||
type: 'asset/resource',
|
||||
generator: {
|
||||
filename: '[name].[contenthash:8].[ext]',
|
||||
},
|
||||
},
|
||||
/* for font-awesome */
|
||||
{
|
||||
test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
|
||||
loader: 'url-loader?limit=10000&mimetype=application/font-woff',
|
||||
options: {
|
||||
esModule: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
|
||||
loader: 'file-loader',
|
||||
options: {
|
||||
esModule: false,
|
||||
},
|
||||
test: /\.(woff|woff2|eot|ttf|otf)$/i,
|
||||
type: 'asset/resource',
|
||||
},
|
||||
{
|
||||
test: /\.ya?ml$/,
|
||||
|
@ -456,20 +433,15 @@ let proxyConfig = getProxyConfig();
|
|||
if (isDevMode) {
|
||||
config.devtool = 'eval-cheap-module-source-map';
|
||||
config.devServer = {
|
||||
before(app, server, compiler) {
|
||||
onBeforeSetupMiddleware(devServer) {
|
||||
// load proxy config when manifest updates
|
||||
const hook = compiler.hooks.webpackManifestPluginAfterEmit;
|
||||
hook.tap('ManifestPlugin', manifest => {
|
||||
const { afterEmit } = getCompilerHooks(devServer.compiler);
|
||||
afterEmit.tap('ManifestPlugin', manifest => {
|
||||
proxyConfig = getProxyConfig(manifest);
|
||||
});
|
||||
},
|
||||
historyApiFallback: true,
|
||||
hot: true,
|
||||
injectClient: false,
|
||||
injectHot: true,
|
||||
inline: true,
|
||||
stats: 'minimal',
|
||||
overlay: true,
|
||||
port: devserverPort,
|
||||
// Only serves bundled files from webpack-dev-server
|
||||
// and proxy everything else to Superset backend
|
||||
|
@ -477,7 +449,11 @@ if (isDevMode) {
|
|||
// functions are called for every request
|
||||
() => proxyConfig,
|
||||
],
|
||||
contentBase: path.join(process.cwd(), '../static/assets'),
|
||||
client: {
|
||||
overlay: { errors: true, warnings: false },
|
||||
logging: 'error',
|
||||
},
|
||||
static: path.join(process.cwd(), '../static/assets'),
|
||||
};
|
||||
|
||||
// make sure to use @emotion/* modules in the root directory
|
||||
|
|
Loading…
Reference in New Issue