如何测试 window.open 被 React 组件调用
How to test window.open is being called from a react component
我有一个反应组件,它呈现一系列其他组件,每个组件都有自己的复选框。
有一个名为 rulesToDownload 的状态挂钩,它以一个空数组开始,并在选中/取消选中复选框时向其中添加/删除 ID。
单击 'download' 按钮时,rulesToDownload 数组被传递给数据函数 DownloadFundDataById,forEach 在数组上,并通过 api 调用为每个值调用 window.open附加的ID。 data函数是导入到组件中的,不是作为prop传入的。
这会导致多个选项卡在数据下载时在关闭之前闪烁。它并不完美,但它确实有效。
我想完成我的测试范围,需要测试该函数是否在单击按钮时被调用,以及它是否执行应有的操作。
感谢任何帮助。
代码如下:
Summary.test.js:
it(`should create correct download array when some rules are selected`, async () => {
global.open = sandbox.spy();
fetch.mockResponseOnce(JSON.stringify(selectedRules));
wrapper = mount(<Summary/>);
await act(async () => {} );
wrapper.update();
wrapper.find('ReportProgressSummary').first().find('input').last().simulate('change', {target: {checked: true}});
wrapper.find('button').first().simulate('click');
expect(global.open).to.have.been.called();
});
我可以确认所有的'find'语句都是正确的,并正确更新检查值。
Summary.js:
const Summary = () => {
const [expand, setExpand] = useState(false);
const [buttonText, setButtonText] = useState("expand other rules");
const [rulesToDownload, setRulesToDownload] = useState([]);
const [data, setData] = useState([]);
const [dataLoadComplete, setDataLoadComplete] = useState(false);
const [dataLoadFailed, setDataLoadFailed] = useState(false);
useEffect(() => {
loadData();
}, []);
const loadData = async () => {
try {
let importedData = await ExecuteRules();
setData(importedData);
setDataLoadComplete(true);
} catch (_) {
setDataLoadFailed(true);
}
};
const onButtonClick = () => {
setExpand(!expand);
if(!expand) setButtonText("hide other rules");
else setButtonText("expand other rules");
};
const modifyDownloadArray = (id, checked) => {
let tempArray;
if(checked) tempArray = [...rulesToDownload, id];
else tempArray = [...rulesToDownload.filter(ruleId => ruleId !== id)];
setRulesToDownload([...tempArray]);
};
const dataFilter = (inputData, isFavouriteValue) => {
return inputData.filter(rule => rule.isFavourite === isFavouriteValue)
.sort((a, b) => a.percentage - b.percentage)
.map((rule, i) => {
return <ReportProgressSummary
result={rule.percentage}
id={rule.id}
title={rule.name} key={i}
modifyDownloadArray={modifyDownloadArray}
/>
})
};
return (
<div className="test">
{
dataLoadFailed &&
<div>Rule load failed</div>
}
{
!dataLoadComplete &&
<LoadingSpinnerTitle holdingTitle="Loading rule data..."/>
}
{
dataLoadComplete &&
<Fragment>
<PageTitle title="System Overview"/>
<LineAndButtonContainerStyled>
<ContainerStyled>
{
dataFilter(data, true)
}
</ContainerStyled>
<ContainerStyled>
<ButtonStyled
disabled={!rulesToDownload.length}
onClick={() => DownloadFundDataById(rulesToDownload)}>
download
</ButtonStyled>
</ContainerStyled>
</LineAndButtonContainerStyled>
<LineBreakStyled/>
<ButtonStyled onClick={() => onButtonClick()}>{buttonText}</ButtonStyled>
{
expand &&
<ContainerStyled>
{
dataFilter(data, false)
}
</ContainerStyled>
}
</Fragment>
}
</div>
)
};
export default Summary;
DataMethod.js:
export function DownloadFundDataById(downloadArray) {
downloadArray.forEach(id => window.open(baseApiUrl + '/xxxx/xxxx/' + id));
}
我可以确认 url 没问题,只是暂时更换了
测试设置:
const doc = jsdom.jsdom('<!doctype html><html><body></body></html>')
global.document = doc;
global.window = doc.defaultView;
configure({ adapter: new Adapter() });
global.expect = expect;
global.sandbox = sinon.createSandbox();
global.React = React;
global.mount = mount;
global.shallow = shallow;
global.render = render;
global.fetch = jestFetchMock;
global.act = act;
chai.use(chaiAsPromised);
chai.use(sinonChai);
chai.use(chaiEnzyme());
chai.use(chaiJestDiff());
console.error = () => {};
console.warn = () => {};
当前测试输出表明 global.open 没有被调用。我知道这是有道理的,因为它实际上并没有作为道具分配给按钮的 onClick 或任何东西。我认为这是我的问题之一 - 我无法直接将存根分配给按钮,但我尽量不重写我的代码以适应我的测试...
设法通过对我的测试文件进行一些更新来实现此功能:
it(`should create correct download array when some rules are selected`, async () => {
global.open = sandbox.stub(window, "open");
fetch.mockResponseOnce(JSON.stringify(selectedRules));
wrapper = mount(<Summary/>);
await act(async () => {} );
wrapper.update();
wrapper.find('ReportProgressSummary').first().find('input').last().simulate('change', {target: {checked: true}});
wrapper.find('button').first().simulate('click');
expect(global.open).to.have.been.called;
});
sandbox.spy() 已更新为 sandbox.stub() with (window, "open")
感谢本文的帮助!
https://github.com/mrdulin/mocha-chai-sinon-codelab/blob/master/src/Whosebug/53524524/index.spec.js
此外,使用 to.be.called() 的 expect 语句实际上不是函数,因此已更新为 to.be.called
我有一个反应组件,它呈现一系列其他组件,每个组件都有自己的复选框。
有一个名为 rulesToDownload 的状态挂钩,它以一个空数组开始,并在选中/取消选中复选框时向其中添加/删除 ID。
单击 'download' 按钮时,rulesToDownload 数组被传递给数据函数 DownloadFundDataById,forEach 在数组上,并通过 api 调用为每个值调用 window.open附加的ID。 data函数是导入到组件中的,不是作为prop传入的。
这会导致多个选项卡在数据下载时在关闭之前闪烁。它并不完美,但它确实有效。
我想完成我的测试范围,需要测试该函数是否在单击按钮时被调用,以及它是否执行应有的操作。
感谢任何帮助。
代码如下:
Summary.test.js:
it(`should create correct download array when some rules are selected`, async () => {
global.open = sandbox.spy();
fetch.mockResponseOnce(JSON.stringify(selectedRules));
wrapper = mount(<Summary/>);
await act(async () => {} );
wrapper.update();
wrapper.find('ReportProgressSummary').first().find('input').last().simulate('change', {target: {checked: true}});
wrapper.find('button').first().simulate('click');
expect(global.open).to.have.been.called();
});
我可以确认所有的'find'语句都是正确的,并正确更新检查值。
Summary.js:
const Summary = () => {
const [expand, setExpand] = useState(false);
const [buttonText, setButtonText] = useState("expand other rules");
const [rulesToDownload, setRulesToDownload] = useState([]);
const [data, setData] = useState([]);
const [dataLoadComplete, setDataLoadComplete] = useState(false);
const [dataLoadFailed, setDataLoadFailed] = useState(false);
useEffect(() => {
loadData();
}, []);
const loadData = async () => {
try {
let importedData = await ExecuteRules();
setData(importedData);
setDataLoadComplete(true);
} catch (_) {
setDataLoadFailed(true);
}
};
const onButtonClick = () => {
setExpand(!expand);
if(!expand) setButtonText("hide other rules");
else setButtonText("expand other rules");
};
const modifyDownloadArray = (id, checked) => {
let tempArray;
if(checked) tempArray = [...rulesToDownload, id];
else tempArray = [...rulesToDownload.filter(ruleId => ruleId !== id)];
setRulesToDownload([...tempArray]);
};
const dataFilter = (inputData, isFavouriteValue) => {
return inputData.filter(rule => rule.isFavourite === isFavouriteValue)
.sort((a, b) => a.percentage - b.percentage)
.map((rule, i) => {
return <ReportProgressSummary
result={rule.percentage}
id={rule.id}
title={rule.name} key={i}
modifyDownloadArray={modifyDownloadArray}
/>
})
};
return (
<div className="test">
{
dataLoadFailed &&
<div>Rule load failed</div>
}
{
!dataLoadComplete &&
<LoadingSpinnerTitle holdingTitle="Loading rule data..."/>
}
{
dataLoadComplete &&
<Fragment>
<PageTitle title="System Overview"/>
<LineAndButtonContainerStyled>
<ContainerStyled>
{
dataFilter(data, true)
}
</ContainerStyled>
<ContainerStyled>
<ButtonStyled
disabled={!rulesToDownload.length}
onClick={() => DownloadFundDataById(rulesToDownload)}>
download
</ButtonStyled>
</ContainerStyled>
</LineAndButtonContainerStyled>
<LineBreakStyled/>
<ButtonStyled onClick={() => onButtonClick()}>{buttonText}</ButtonStyled>
{
expand &&
<ContainerStyled>
{
dataFilter(data, false)
}
</ContainerStyled>
}
</Fragment>
}
</div>
)
};
export default Summary;
DataMethod.js:
export function DownloadFundDataById(downloadArray) {
downloadArray.forEach(id => window.open(baseApiUrl + '/xxxx/xxxx/' + id));
}
我可以确认 url 没问题,只是暂时更换了
测试设置:
const doc = jsdom.jsdom('<!doctype html><html><body></body></html>')
global.document = doc;
global.window = doc.defaultView;
configure({ adapter: new Adapter() });
global.expect = expect;
global.sandbox = sinon.createSandbox();
global.React = React;
global.mount = mount;
global.shallow = shallow;
global.render = render;
global.fetch = jestFetchMock;
global.act = act;
chai.use(chaiAsPromised);
chai.use(sinonChai);
chai.use(chaiEnzyme());
chai.use(chaiJestDiff());
console.error = () => {};
console.warn = () => {};
当前测试输出表明 global.open 没有被调用。我知道这是有道理的,因为它实际上并没有作为道具分配给按钮的 onClick 或任何东西。我认为这是我的问题之一 - 我无法直接将存根分配给按钮,但我尽量不重写我的代码以适应我的测试...
设法通过对我的测试文件进行一些更新来实现此功能:
it(`should create correct download array when some rules are selected`, async () => {
global.open = sandbox.stub(window, "open");
fetch.mockResponseOnce(JSON.stringify(selectedRules));
wrapper = mount(<Summary/>);
await act(async () => {} );
wrapper.update();
wrapper.find('ReportProgressSummary').first().find('input').last().simulate('change', {target: {checked: true}});
wrapper.find('button').first().simulate('click');
expect(global.open).to.have.been.called;
});
sandbox.spy() 已更新为 sandbox.stub() with (window, "open")
感谢本文的帮助!
https://github.com/mrdulin/mocha-chai-sinon-codelab/blob/master/src/Whosebug/53524524/index.spec.js
此外,使用 to.be.called() 的 expect 语句实际上不是函数,因此已更新为 to.be.called