簡體   English   中英

開玩笑+酶+反應16: <img> src:請求未發送

[英]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' },

調試步驟:

  1. 我已經確認Canvas已成功安裝並在jsdom中運行良好。

  2. 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')”的功能中請求已處理。

  1. 結果顯示jsdom“窗口”在真正的HTTP請求開始傳出之前關閉,然后,此請求的狀態被設置為“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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM