[英]jest + enzyme + react16: <img> src : request not sent
我正在使用jest + enzyme來測試我的反應組件“AnimateImage”,其中包含一個圖像元素:
import * as React from 'react';
import { PureComponent } from 'react';
interface Props {
src: string;
}
class AnimateImage extends PureComponent<Props> {
onImgLoad = (e: Event | {target: HTMLImageElement}) => {
console.log("yes!");
};
render() {
return (
<div className="app-image-container">
<img
ref={c => {
if (!c) {
return;
}
c.onerror = function(e){
console.log("error:" , e);
}
if(!c.onload){
c.onload = this.onImgLoad;
if (c && c.complete && c.naturalWidth !== 0) {
this.onImgLoad({
target: c
})
}
}
}}
src={this.props.src}
/>
</div>
);
}
}
export default AnimateImage;
測試代碼:
test("image ", () => {
const component = mount(<AnimateImage src={url_test}/>);
expect(component).toMatchSnapshot();
console.log("end ##################################################################");
})
預期的結果:
調用圖像的onload處理程序,我可以看到“是的!” 打印在控制台中。
真實的結果:
不調用圖像的onload處理程序,並且圖像的完整屬性為false。
我的開玩笑配置:
verbose: true,
transform: {
'.(ts|tsx)': 'ts-jest'
},
snapshotSerializers: ['enzyme-to-json/serializer'],
moduleFileExtensions: ['ts', 'tsx', 'js', 'json'],
testEnvironment: "jest-environment-jsdom-fourteen",
testEnvironmentOptions: { "resources": 'usable' },
調試步驟:
我已經確認Canvas已成功安裝並在jsdom中運行良好。
jsdom的resource-loader使用“request-promise-native”包來獲取HTTP資源。 “request-promise-native”包的核心是“request”包。
在“request”包中,request.js文件聲明了一個名為Request的類來處理HTTP請求。
但是我發現從不調用Request.start()函數,並且在請求的狀態為“abort”的情況下調用defer函數。
順便說一下,我把兩個“console.log()”放在模擬“窗口”和“文檔”調用“關閉”功能和“console.log('abort')”的功能中請求已處理。
bogon: yarn test:dom
yarn run v1.10.1
$ jest --config jest.config.js
PASS animate-image.spec.tsx
✓ image (75ms)
console.log xxxxxxxxx/animate-image.spec.tsx:34
end ##################################################################
window close
document close
http://XXXXX.cdn.com
abort
request.js中的一些代碼可能有助於理解問題:
var defer = typeof setImmediate === 'undefined'
? process.nextTick
: setImmediate
defer(function () {
if (self._aborted) {
return
}
var end = function () {
if (self._form) {
if (!self._auth.hasAuth) {
self._form.pipe(self)
} else if (self._auth.hasAuth && self._auth.sentAuth) {
self._form.pipe(self)
}
}
if (self._multipart && self._multipart.chunked) {
self._multipart.body.pipe(self)
}
if (self.body) {
if (isstream(self.body)) {
self.body.pipe(self)
} else {
setContentLength()
if (Array.isArray(self.body)) {
self.body.forEach(function (part) {
self.write(part)
})
} else {
self.write(self.body)
}
self.end()
}
} else if (self.requestBodyStream) {
console.warn('options.requestBodyStream is deprecated, please pass the request object to stream.pipe.')
self.requestBodyStream.pipe(self)
} else if (!self.src) {
if (self._auth.hasAuth && !self._auth.sentAuth) {
self.end()
return
}
if (self.method !== 'GET' && typeof self.method !== 'undefined') {
self.setHeader('content-length', 0)
}
self.end()
}
}
if (self._form && !self.hasHeader('content-length')) {
// Before ending the request, we had to compute the length of the whole form, asyncly
self.setHeader(self._form.getHeaders(), true)
self._form.getLength(function (err, length) {
if (!err && !isNaN(length)) {
self.setHeader('content-length', length)
}
end()
})
} else {
end()
}
self.ntick = true
})
Request.prototype.start = function () {
// start() is called once we are ready to send the outgoing HTTP request.
// this is usually called on the first write(), end() or on nextTick()
var self = this
if (self.timing) {
// All timings will be relative to this request's startTime. In order to do this,
// we need to capture the wall-clock start time (via Date), immediately followed
// by the high-resolution timer (via now()). While these two won't be set
// at the _exact_ same time, they should be close enough to be able to calculate
// high-resolution, monotonically non-decreasing timestamps relative to startTime.
var startTime = new Date().getTime()
var startTimeNow = now()
}
if (self._aborted) {
return
}
self._started = true
self.method = self.method || 'GET'
self.href = self.uri.href
if (self.src && self.src.stat && self.src.stat.size && !self.hasHeader('content-length')) {
self.setHeader('content-length', self.src.stat.size)
}
if (self._aws) {
self.aws(self._aws, true)
}
// We have a method named auth, which is completely different from the http.request
// auth option. If we don't remove it, we're gonna have a bad time.
var reqOptions = copy(self)
delete reqOptions.auth
debug('make request', self.uri.href)
// node v6.8.0 now supports a `timeout` value in `http.request()`, but we
// should delete it for now since we handle timeouts manually for better
// consistency with node versions before v6.8.0
delete reqOptions.timeout
try {
self.req = self.httpModule.request(reqOptions)
} catch (err) {
self.emit('error', err)
return
}
if (self.timing) {
self.startTime = startTime
self.startTimeNow = startTimeNow
// Timing values will all be relative to startTime (by comparing to startTimeNow
// so we have an accurate clock)
self.timings = {}
}
var timeout
if (self.timeout && !self.timeoutTimer) {
if (self.timeout < 0) {
timeout = 0
} else if (typeof self.timeout === 'number' && isFinite(self.timeout)) {
timeout = self.timeout
}
}
self.req.on('response', self.onRequestResponse.bind(self))
self.req.on('error', self.onRequestError.bind(self))
self.req.on('drain', function () {
self.emit('drain')
})
self.req.on('socket', function (socket) {
// `._connecting` was the old property which was made public in node v6.1.0
var isConnecting = socket._connecting || socket.connecting
if (self.timing) {
self.timings.socket = now() - self.startTimeNow
if (isConnecting) {
var onLookupTiming = function () {
self.timings.lookup = now() - self.startTimeNow
}
var onConnectTiming = function () {
self.timings.connect = now() - self.startTimeNow
}
socket.once('lookup', onLookupTiming)
socket.once('connect', onConnectTiming)
// clean up timing event listeners if needed on error
self.req.once('error', function () {
socket.removeListener('lookup', onLookupTiming)
socket.removeListener('connect', onConnectTiming)
})
}
}
var setReqTimeout = function () {
// This timeout sets the amount of time to wait *between* bytes sent
// from the server once connected.
//
// In particular, it's useful for erroring if the server fails to send
// data halfway through streaming a response.
self.req.setTimeout(timeout, function () {
if (self.req) {
self.abort()
var e = new Error('ESOCKETTIMEDOUT')
e.code = 'ESOCKETTIMEDOUT'
e.connect = false
self.emit('error', e)
}
})
}
if (timeout !== undefined) {
// Only start the connection timer if we're actually connecting a new
// socket, otherwise if we're already connected (because this is a
// keep-alive connection) do not bother. This is important since we won't
// get a 'connect' event for an already connected socket.
if (isConnecting) {
var onReqSockConnect = function () {
socket.removeListener('connect', onReqSockConnect)
clearTimeout(self.timeoutTimer)
self.timeoutTimer = null
setReqTimeout()
}
socket.on('connect', onReqSockConnect)
self.req.on('error', function (err) { // eslint-disable-line handle-callback-err
socket.removeListener('connect', onReqSockConnect)
})
// Set a timeout in memory - this block will throw if the server takes more
// than `timeout` to write the HTTP status and headers (corresponding to
// the on('response') event on the client). NB: this measures wall-clock
// time, not the time between bytes sent by the server.
self.timeoutTimer = setTimeout(function () {
socket.removeListener('connect', onReqSockConnect)
self.abort()
var e = new Error('ETIMEDOUT')
e.code = 'ETIMEDOUT'
e.connect = true
self.emit('error', e)
}, timeout)
} else {
// We're already connected
setReqTimeout()
}
}
self.emit('socket', socket)
})
self.emit('request', self.req)
}
我無法獲取發送的HTTP請求來獲取圖像源。 因此我無法調用img.onload處理程序。
有誰能幫我解釋一下這個問題?
最后,我沒有找到成功發送加載圖像請求的方法。
我的解決方案是:在我的測試代碼中模擬HTMLImageElement的原型:
Object.defineProperty(HTMLImageElement.prototype, 'naturalWidth', { get: () => 120 });
Object.defineProperty(HTMLImageElement.prototype, 'complete', { get: () => true });
因此,我不再需要獲得真實的圖像,同時我可以成功完成我的測試用例。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.