mirror of
https://github.com/gosticks/web.git
synced 2026-10-06 14:47:32 +00:00
Merge branch 'development' into tweak/opendns-ipv6
This commit is contained in:
Generated
+15
-15
@@ -2610,9 +2610,9 @@
|
||||
}
|
||||
},
|
||||
"@types/jest": {
|
||||
"version": "24.0.13",
|
||||
"resolved": "https://registry.npmjs.org/@types/jest/-/jest-24.0.13.tgz",
|
||||
"integrity": "sha512-3m6RPnO35r7Dg+uMLj1+xfZaOgIHHHut61djNjzwExXN4/Pm9has9C6I1KMYSfz7mahDhWUOVg4HW/nZdv5Pww==",
|
||||
"version": "24.0.14",
|
||||
"resolved": "https://registry.npmjs.org/@types/jest/-/jest-24.0.14.tgz",
|
||||
"integrity": "sha512-IxS2AO0nOr4zrpKfRCxobQUb1bSK6ejodZ7odCzHXMjsASCI8J10N8qVQhrCjvJTc3bUjGGeuD+ytKZqyhajqQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/jest-diff": "*"
|
||||
@@ -2676,9 +2676,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@types/react": {
|
||||
"version": "16.8.19",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.8.19.tgz",
|
||||
"integrity": "sha512-QzEzjrd1zFzY9cDlbIiFvdr+YUmefuuRYrPxmkwG0UQv5XF35gFIi7a95m1bNVcFU0VimxSZ5QVGSiBmlggQXQ==",
|
||||
"version": "16.8.20",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.8.20.tgz",
|
||||
"integrity": "sha512-ZLmI+ubSJpfUIlQuULDDrdyuFQORBuGOvNnMue8HeA0GVrAJbWtZQhcBvnBPNRBI/GrfSfrKPFhthzC2SLEtLQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/prop-types": "*",
|
||||
@@ -2714,9 +2714,9 @@
|
||||
}
|
||||
},
|
||||
"@types/react-router": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.0.1.tgz",
|
||||
"integrity": "sha512-vOyVO0u3Cs0w6G5DzYqNVqcTsurEnDgOmmkJf2s7VwtunWzpPgI6dHsCBX68vXqeICpP6jCfojgJcHkm5BV7hQ==",
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.0.2.tgz",
|
||||
"integrity": "sha512-sdMN284GEOcqDEMS/hE/XD06Abw2fws30+xkZf3C9cSRcWopiv/HDTmunYI7DKLYKVRaWFkq1lkuJ6qeYu0E7A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/history": "*",
|
||||
@@ -2724,9 +2724,9 @@
|
||||
}
|
||||
},
|
||||
"@types/react-router-dom": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-4.3.3.tgz",
|
||||
"integrity": "sha512-xj0DmFjgvAqRfh/kJPO7apD5G30yPQe+8slu/dugioQOkdKpyzc4Fgk4hoTelm6CSHz7pI2PPsW5+Y6GRBF2zw==",
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-4.3.4.tgz",
|
||||
"integrity": "sha512-xrwaWHpnxKk/TTRe7pmoGy3E4SyF/ojFqNfFJacw7OLdfLXRvGfk4r/XePVaZNVfeJzL8fcnNilPN7xOdJ/vGw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/history": "*",
|
||||
@@ -21687,9 +21687,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"typescript": {
|
||||
"version": "3.5.1",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.5.1.tgz",
|
||||
"integrity": "sha512-64HkdiRv1yYZsSe4xC1WVgamNigVYjlssIoaH2HcZF0+ijsk5YK2g0G34w9wJkze8+5ow4STd22AynfO6ZYYLw==",
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.5.2.tgz",
|
||||
"integrity": "sha512-7KxJovlYhTX5RaRbUdkAXN1KUZ8PwWlTzQdHV6xNqvuFOs7+WBo10TQUqT19Q/Jz2hk5v9TQDIhyLhhJY4p5AA==",
|
||||
"dev": true
|
||||
},
|
||||
"uglify-js": {
|
||||
|
||||
+4
-4
@@ -8,15 +8,15 @@
|
||||
"@types/enzyme": "^3.9.3",
|
||||
"@types/enzyme-adapter-react-16": "^1.0.5",
|
||||
"@types/fetch-mock": "^7.3.0",
|
||||
"@types/jest": "^24.0.13",
|
||||
"@types/jest": "^24.0.14",
|
||||
"@types/lodash.debounce": "^4.0.6",
|
||||
"@types/lodash.isequal": "^4.5.5",
|
||||
"@types/node": "^12.0.8",
|
||||
"@types/react": "^16.8.19",
|
||||
"@types/react": "^16.8.20",
|
||||
"@types/react-bootstrap-daterangepicker": "0.0.26",
|
||||
"@types/react-bootstrap-typeahead": "^3.4.5",
|
||||
"@types/react-dom": "^16.8.4",
|
||||
"@types/react-router-dom": "^4.3.3",
|
||||
"@types/react-router-dom": "^4.3.4",
|
||||
"@types/react-table": "^6.8.3",
|
||||
"@types/reactstrap": "^8.0.1",
|
||||
"@types/sha.js": "^2.4.0",
|
||||
@@ -34,7 +34,7 @@
|
||||
"prettier": "^1.18.2",
|
||||
"react-scripts": "^3.0.1",
|
||||
"react-test-renderer": "^16.8.6",
|
||||
"typescript": "^3.5.1"
|
||||
"typescript": "^3.5.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@coreui/coreui": "^2.1.12",
|
||||
|
||||
@@ -102,9 +102,7 @@ export class WithAPIData<T> extends Component<
|
||||
if (cancelOptions) {
|
||||
// If the request should be repeated, wait for the interval and then
|
||||
// refresh with data from the API
|
||||
new Promise(resolve =>
|
||||
setTimeout(resolve, cancelOptions.interval)
|
||||
).then(() => this.loadData());
|
||||
setTimeout(() => this.loadData(), cancelOptions.interval);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,321 @@
|
||||
/* Pi-hole: A black hole for Internet advertisements
|
||||
* (c) 2019 Pi-hole, LLC (https://pi-hole.net)
|
||||
* Network-wide ad blocking via your own hardware.
|
||||
*
|
||||
* Web Interface
|
||||
* WithAPIData component test
|
||||
*
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
import React from "react";
|
||||
import { shallow, ShallowWrapper } from "enzyme";
|
||||
import {
|
||||
WithAPIData,
|
||||
WithAPIDataProps,
|
||||
WithAPIDataState
|
||||
} from "../WithAPIData";
|
||||
|
||||
const tick = global.tick;
|
||||
const emptyRender = () => null;
|
||||
const emptyAPICall = () => Promise.resolve({});
|
||||
|
||||
type WithAPIDataWrapper<T> = ShallowWrapper<
|
||||
WithAPIDataProps<T>,
|
||||
WithAPIDataState<T>,
|
||||
WithAPIData<T>
|
||||
>;
|
||||
|
||||
it("should use renderInitial at the start", () => {
|
||||
const renderInitial = jest.fn(() => "test");
|
||||
const wrapper = shallow(
|
||||
<WithAPIData
|
||||
renderErr={emptyRender}
|
||||
renderInitial={renderInitial}
|
||||
apiCall={emptyAPICall}
|
||||
renderOk={emptyRender}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(renderInitial).toHaveBeenCalled();
|
||||
expect(wrapper).toHaveText("test");
|
||||
});
|
||||
|
||||
it("should use renderOk after fetching the data", async () => {
|
||||
const renderOk = jest.fn(data => data);
|
||||
const wrapper = shallow(
|
||||
<WithAPIData
|
||||
renderErr={emptyRender}
|
||||
renderInitial={emptyRender}
|
||||
apiCall={() => Promise.resolve("test")}
|
||||
renderOk={renderOk}
|
||||
/>
|
||||
);
|
||||
|
||||
// Let the API call resolve
|
||||
await tick();
|
||||
|
||||
expect(renderOk).toHaveBeenCalled();
|
||||
expect(wrapper).toHaveText("test");
|
||||
});
|
||||
|
||||
it("should cancel the in-flight request when unmounting", () => {
|
||||
const renderOk = jest.fn(data => data);
|
||||
const apiCall = jest.fn(emptyAPICall);
|
||||
const wrapper = shallow(
|
||||
<WithAPIData
|
||||
renderErr={emptyRender}
|
||||
renderInitial={emptyRender}
|
||||
apiCall={apiCall}
|
||||
renderOk={renderOk}
|
||||
/>
|
||||
);
|
||||
|
||||
wrapper.unmount();
|
||||
|
||||
expect(apiCall).toHaveBeenCalled();
|
||||
expect(renderOk).not.toHaveBeenCalled();
|
||||
expect(wrapper).not.toHaveText("test");
|
||||
});
|
||||
|
||||
it("should only rerender once if refresh is called twice", async () => {
|
||||
let called = false;
|
||||
const renderOk = jest.fn((_, refresh) => {
|
||||
if (called) return null;
|
||||
called = true;
|
||||
refresh();
|
||||
refresh();
|
||||
return null;
|
||||
});
|
||||
|
||||
shallow(
|
||||
<WithAPIData
|
||||
renderErr={emptyRender}
|
||||
renderInitial={emptyRender}
|
||||
apiCall={emptyAPICall}
|
||||
renderOk={renderOk}
|
||||
/>
|
||||
);
|
||||
|
||||
// Let the two API calls resolve
|
||||
await tick();
|
||||
await tick();
|
||||
|
||||
expect(renderOk).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it("should ignore cancel errors if the option is enabled", async () => {
|
||||
const apiCall = () => Promise.reject({ isCanceled: true });
|
||||
const renderOk = jest.fn(emptyRender);
|
||||
const renderErr = jest.fn(emptyRender);
|
||||
|
||||
shallow(
|
||||
<WithAPIData
|
||||
renderErr={renderErr}
|
||||
renderInitial={emptyRender}
|
||||
apiCall={apiCall}
|
||||
renderOk={renderOk}
|
||||
repeatOptions={{ ignoreCancel: true, interval: 0 }}
|
||||
/>
|
||||
);
|
||||
|
||||
// Let the API call resolve
|
||||
await tick();
|
||||
|
||||
// If the request was canceled, neither the Ok nor Err renderers would have
|
||||
// been called
|
||||
expect(renderErr).not.toHaveBeenCalled();
|
||||
expect(renderOk).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should throw non-cancel errors when ignoring cancel errors", async () => {
|
||||
const error = { test: true };
|
||||
const apiCall = () => Promise.reject(error);
|
||||
const renderOk = jest.fn(emptyRender);
|
||||
const renderErr = jest.fn(() => "test");
|
||||
|
||||
const wrapper: WithAPIDataWrapper<null> = shallow(
|
||||
<WithAPIData
|
||||
renderErr={renderErr}
|
||||
renderInitial={emptyRender}
|
||||
apiCall={apiCall}
|
||||
renderOk={renderOk}
|
||||
repeatOptions={{ ignoreCancel: true, interval: 0 }}
|
||||
/>
|
||||
);
|
||||
|
||||
// Let the API call resolve
|
||||
await tick();
|
||||
|
||||
expect(renderErr).toHaveBeenCalledWith(error, wrapper.instance().loadData);
|
||||
expect(renderOk).not.toHaveBeenCalled();
|
||||
expect(wrapper).toHaveText("test");
|
||||
});
|
||||
|
||||
it("should throw cancel errors when not ignoring cancel", async () => {
|
||||
const error = { isCanceled: true };
|
||||
const apiCall = () => Promise.reject(error);
|
||||
const renderOk = jest.fn(emptyRender);
|
||||
const renderErr = jest.fn(() => "test");
|
||||
|
||||
const wrapper: WithAPIDataWrapper<null> = shallow(
|
||||
<WithAPIData
|
||||
renderErr={renderErr}
|
||||
renderInitial={emptyRender}
|
||||
apiCall={apiCall}
|
||||
renderOk={renderOk}
|
||||
repeatOptions={{ ignoreCancel: false, interval: 0 }}
|
||||
/>
|
||||
);
|
||||
|
||||
// Let the API call resolve
|
||||
await tick();
|
||||
|
||||
expect(renderErr).toHaveBeenCalledWith(error, wrapper.instance().loadData);
|
||||
expect(renderOk).not.toHaveBeenCalled();
|
||||
expect(wrapper).toHaveText("test");
|
||||
});
|
||||
|
||||
it("should clear the data when flushOnUpdate is true and props are changed", async () => {
|
||||
const renderInitial = jest.fn(() => "initial");
|
||||
const renderOk = jest.fn(() => "ok");
|
||||
const apiCall = jest.fn(() => Promise.resolve("test"));
|
||||
const wrapper: WithAPIDataWrapper<string> = shallow(
|
||||
<WithAPIData
|
||||
renderErr={emptyRender}
|
||||
renderInitial={renderInitial}
|
||||
apiCall={apiCall}
|
||||
renderOk={renderOk}
|
||||
repeatOptions={{ ignoreCancel: true, interval: 0 }}
|
||||
flushOnUpdate={true}
|
||||
/>
|
||||
);
|
||||
|
||||
// Let the API call resolve
|
||||
await tick();
|
||||
|
||||
expect(apiCall).toBeCalledTimes(1);
|
||||
expect(renderInitial).toHaveBeenCalledTimes(1);
|
||||
expect(renderOk).toHaveBeenCalledTimes(1);
|
||||
|
||||
// Change the props in a small way
|
||||
wrapper.setProps({ repeatOptions: { ignoreCancel: false, interval: 0 } });
|
||||
|
||||
// Let the second API call resolve
|
||||
await tick();
|
||||
|
||||
expect(apiCall).toBeCalledTimes(2);
|
||||
expect(renderInitial).toHaveBeenCalledTimes(2);
|
||||
expect(renderOk).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
|
||||
it("should use the provided data on refresh instead of hitting the API", async () => {
|
||||
let refreshTest: ((data: any) => void) | undefined = undefined;
|
||||
const renderOk = jest.fn((data, refresh) => {
|
||||
refreshTest = refresh;
|
||||
return data;
|
||||
});
|
||||
const apiCall = jest.fn(() => Promise.resolve("test data"));
|
||||
const wrapper = shallow(
|
||||
<WithAPIData
|
||||
renderErr={emptyRender}
|
||||
renderInitial={emptyRender}
|
||||
apiCall={apiCall}
|
||||
renderOk={renderOk}
|
||||
/>
|
||||
);
|
||||
|
||||
// Let the API call resolve
|
||||
await tick();
|
||||
|
||||
// Call the refresh function with the new data
|
||||
expect(refreshTest).toBeDefined();
|
||||
refreshTest!("new data");
|
||||
|
||||
expect(apiCall).toHaveBeenCalledTimes(1);
|
||||
expect(renderOk).toHaveBeenCalledTimes(2);
|
||||
expect(wrapper).toHaveText("new data");
|
||||
});
|
||||
|
||||
it("should wait for the interval after refreshing with provided data", async () => {
|
||||
jest.useFakeTimers();
|
||||
|
||||
let refreshTest: ((data: any) => void) | undefined = undefined;
|
||||
const renderOk = jest.fn((data, refresh) => {
|
||||
refreshTest = refresh;
|
||||
return data;
|
||||
});
|
||||
const apiCall = jest.fn(() => Promise.resolve("test data"));
|
||||
|
||||
shallow(
|
||||
<WithAPIData
|
||||
renderErr={emptyRender}
|
||||
renderInitial={emptyRender}
|
||||
apiCall={apiCall}
|
||||
renderOk={renderOk}
|
||||
repeatOptions={{ ignoreCancel: true, interval: 1000 }}
|
||||
/>
|
||||
);
|
||||
|
||||
// Let the API call resolve
|
||||
await tick();
|
||||
|
||||
// Call the refresh function with the new data
|
||||
expect(refreshTest).toBeDefined();
|
||||
refreshTest!("new data");
|
||||
|
||||
// Wait for the interval
|
||||
jest.advanceTimersByTime(1000);
|
||||
|
||||
// The API call should have been triggered again
|
||||
expect(apiCall).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it("should pass through repeat options", async () => {
|
||||
jest.useFakeTimers();
|
||||
|
||||
const wrapper: WithAPIDataWrapper<null> = shallow(
|
||||
<WithAPIData
|
||||
renderErr={emptyRender}
|
||||
renderInitial={emptyRender}
|
||||
apiCall={emptyAPICall}
|
||||
renderOk={emptyRender}
|
||||
repeatOptions={{ ignoreCancel: true, interval: 1000 }}
|
||||
/>
|
||||
);
|
||||
|
||||
// Let the API call resolve
|
||||
await tick();
|
||||
|
||||
expect(setTimeout).toHaveBeenCalledWith(wrapper.instance().loadData, 1000);
|
||||
});
|
||||
|
||||
it("should pass through repeat options after refresh", async () => {
|
||||
jest.useFakeTimers();
|
||||
|
||||
let refreshTest: ((data: any) => void) | undefined = undefined;
|
||||
const renderOk = jest.fn((data, refresh) => {
|
||||
refreshTest = refresh;
|
||||
return data;
|
||||
});
|
||||
const apiCall = jest.fn(() => Promise.resolve("test data"));
|
||||
|
||||
shallow(
|
||||
<WithAPIData
|
||||
renderErr={emptyRender}
|
||||
renderInitial={emptyRender}
|
||||
apiCall={apiCall}
|
||||
renderOk={renderOk}
|
||||
repeatOptions={{ ignoreCancel: true, interval: 1000 }}
|
||||
/>
|
||||
);
|
||||
|
||||
// Let the API call resolve
|
||||
await tick();
|
||||
|
||||
// Call the refresh function with the new data
|
||||
expect(refreshTest).toBeDefined();
|
||||
refreshTest!("new data");
|
||||
|
||||
expect(setTimeout).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
import React, { Component } from "react";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import { Form, Col, Input, FormGroup, Label } from "reactstrap";
|
||||
import api from "../../util/api";
|
||||
import { WithAPIData } from "../common/WithAPIData";
|
||||
|
||||
@@ -24,13 +25,47 @@ class FTLInfo extends Component<FTLInfoProps & WithTranslation, {}> {
|
||||
const { t } = this.props;
|
||||
|
||||
return (
|
||||
<pre>
|
||||
{t("Queries")}: {this.props.queries}
|
||||
<br />
|
||||
{t("Filesize")}: {this.props.fileSize.toLocaleString()} B<br />
|
||||
{t("SQLite version")}: {this.props.sqliteVersion.toLocaleString()}
|
||||
<br />
|
||||
</pre>
|
||||
<Form>
|
||||
<FormGroup row>
|
||||
<Label className="bold" for="queries" sm={4}>
|
||||
{t("Queries")}
|
||||
</Label>
|
||||
<Col sm={8}>
|
||||
<Input
|
||||
plaintext
|
||||
readOnly
|
||||
id="queries"
|
||||
value={this.props.queries.toLocaleString()}
|
||||
/>
|
||||
</Col>
|
||||
</FormGroup>
|
||||
<FormGroup row>
|
||||
<Label className="bold" for="filesize" sm={4}>
|
||||
{t("Filesize")}
|
||||
</Label>
|
||||
<Col sm={8}>
|
||||
<Input
|
||||
plaintext
|
||||
readOnly
|
||||
id="filesize"
|
||||
value={`${this.props.fileSize.toLocaleString()} B`}
|
||||
/>
|
||||
</Col>
|
||||
</FormGroup>
|
||||
<FormGroup row>
|
||||
<Label className="bold" for="sqliteversion" sm={4}>
|
||||
{t("SQLite version")}
|
||||
</Label>
|
||||
<Col sm={8}>
|
||||
<Input
|
||||
plaintext
|
||||
readOnly
|
||||
id="sqliteversion"
|
||||
value={this.props.sqliteVersion}
|
||||
/>
|
||||
</Col>
|
||||
</FormGroup>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user