Skip to content
This repository has been archived by the owner on Jun 3, 2024. It is now read-only.

What about images? #11

Closed
GantMan opened this issue Feb 15, 2016 · 28 comments
Closed

What about images? #11

GantMan opened this issue Feb 15, 2016 · 28 comments

Comments

@GantMan
Copy link

GantMan commented Feb 15, 2016

I have a file where I require images, but I get the error:

Error: Cannot find module '../Images/Logo/some_image.png'
    at Function.Module._resolveFilename (module.js:338:15)

any way we can mock this?

@Jeiwan
Copy link
Contributor

Jeiwan commented Feb 15, 2016

I used https://github.com/mfncooper/mockery to solve the same issue. It allows you to globally mock require, so you can do:

mockery.enable();
mockery.registerMock('../Images/Logo/some_image.png', 'nice image');

In your tests and this will affect all the files that require '../Images/Logo/some_image.png'. require('../Images/Logo/some_image.png') then will return 'nice image'.

Another similar tool is https://github.com/thlorenz/proxyquire

@GantMan
Copy link
Author

GantMan commented Feb 15, 2016

thanks!

@lelandrichardson
Copy link
Collaborator

This is a great question. Perhaps we could build a method where you can pass it a glob string for images and it will automatically register them as image objects in the require cache.

Open to hear anyone else's suggestions as well...

@iyawnis
Copy link
Contributor

iyawnis commented Feb 17, 2016

If I use mockery tests are working, but instead of a dir error I was getting the following:

Unexpected character '�' (1:0)
  SyntaxError: test/images/reactNe.png: Unexpected character '�' (1:0)
  > 1 | �PNG
      | ^
    2 | 
    3 | 
    4 | IHDR3�W�SgAMA��
                       �asRGB��� cHRMz&�����u0�`:�p��Q<bKGD�������  pHYsHHF�k>n�IDATx���u�V�ڇ���A���������X����BQ��cwca�b���
      at Parser.pp.raise (node_modules/babylon/index.js:1425:13)

@Jeiwan
Copy link
Contributor

Jeiwan commented Feb 17, 2016

@latusaki you cannot require an image in JS. React Native seems to have some kind of a wrapper around require that catches all paths pointing to an image and processes them in a correct way. If you mock image requiring, you should just return a string that somehow relates to the required file (its name, for example), so you could test this string in a component and check that a correct file was required.

@jdebbink
Copy link

@Jeiwan
How are you getting mockery to work? I am getting the same error as @latusaki .

In my module I have:
<Image source={require('./images/temp.png')}/>

In my test file I have:

var mockery = require('mockery');

mockery.enable();
mockery.registerMock('./images/temp.png', 'temp.png');
const Temp = require('../Temp.js');
mockery.disable();

describe('<Temp/>', () => {
  ...
});

@iyawnis
Copy link
Contributor

iyawnis commented Feb 24, 2016

I guess you have tried not disabling mockery?

@jdebbink
Copy link

@latusaki That worked, thanks!

@iyawnis
Copy link
Contributor

iyawnis commented Feb 24, 2016

If I remember correct the effect of mockery does not persist between test cases, so I have just added it like this:

  before(() => {
    mockery.enable();
    mockery.registerMock('../../../images/logo-pink.png', 'logo-ping.png');
  });

@slamus
Copy link

slamus commented Apr 21, 2016

Hey Guys,

Thanks for your help I've been able to fix the require problem.
However, none of your solutions worked for me because required propType for Image.source is either number or shape (?)
So, my solution was this:

import mockery from "mockery";

mockery.enable();
mockery.registerMock('../img/myimage.png', 0)

@RealOrangeOne
Copy link
Owner

@slamus The best way to mock an image objects source would be to pass it something it's expecting.

mockery.registerMock('../img/myimage.png', {{ uri: 'myimage.png' }});

The image doesnt have to exist or anything, but it's a better way of mocking it than just passing in a random value

@Sh3rawi
Copy link

Sh3rawi commented Apr 30, 2016

I ran into the same issue today, and what i did was the following (im using jest):
node_modules/babel-jest/src/index.js:

  process(src, filename) {
    if (babel.util.canCompile(filename)) {
      return babel.transform(src, {
        auxiliaryCommentBefore: ' istanbul ignore next ',
        filename,
        presets: [jestPreset],
        retainLines: true,
      }).code;
    }
    return src;
  },

but it can't compile images, so it returns the source (which is the image itself).
All my images are pngs, so i added the following check:

    if (filename.match(/\.png$/)) {
      const w = filename.match(/(.+\/)(.+png$)/)[2];
      const source = `module.exports = '${w}';`;
      return babel.transform(source, {
        auxiliaryCommentBefore: ' istanbul ignore next ',
        filename,
        presets: [jestPreset],
        retainLines: true,
      }).code;
    }

So i return a string for every image require i have, which is very convenient for testing:
i shallow render the component, get a reference to the image and check the source prop string to be the right image name.

@benjick
Copy link

benjick commented May 2, 2016

Hey, trying your solutions here, but not sure what I'm doing wrong.

test/Button.js

import React, { View, Text, StyleSheet } from 'react-native';
import mockery from "mockery";
import { shallow } from 'enzyme';
import { expect } from 'chai';

mockery.enable();
mockery.registerMock('../src/Theme/assets/closeWhite.png', { uri: 'myimage.png' }); //

import { CloseButton } from '../src';

describe('<CloseButton />', () => {
  it('should render stuff', () => {
    const wrapper = shallow(<CloseButton />);
    expect(wrapper.length).to.equal(1);
  });
});

/*
/node_modules/babel-core/lib/transformation/file/index.js:556
      throw err;
      ^

/src/Theme/assets/closeWhite.png: Unexpected character '�' (1:0)
> 1 | �PNG
    | ^
*/

@RealOrangeOne
Copy link
Owner

@benjick Can you post the source of the CloseButton component too? Remember the first arguement of mockery should be the exact string you try and require, not a relative path to the file

@miracle2k
Copy link

I didn't want to mock all image paths manually, so I made this hook:

var m = require('module');
var originalLoader = m._load;

m._load = function hookedLoader(request, parent, isMain) {
  var file = m._resolveFilename(request, parent);
  if (file.match(/.jpeg|.jpg|.png$/))
    return {uri: file};

  return originalLoader(request, parent, isMain);
}

@RealOrangeOne
Copy link
Owner

@miracle2k oohh that's really nice! Could you submit that as a PR? Maybe putting it in mock.js?

@sibelius
Copy link
Contributor

@miracle2k how can I use this hook with react-native-mock?

@sibelius
Copy link
Contributor

hey @slamus can u provide a repo with your solution?

@sibelius
Copy link
Contributor

@RealOrangeOne I think we should create a gitter room to discuss solutions for these issues

@RealOrangeOne
Copy link
Owner

I was thinking about doing something like that, let me look into it further before I create something

@sibelius
Copy link
Contributor

for react-native-router-flux to run I've used this commands with Mockery on testHelper.js

import mockery from 'mockery';

mockery.enable();

mockery.registerMock('./menu_burger.png', 'burguer');
mockery.registerMock('./back_chevron.png', 'chevron');

@varmais
Copy link
Contributor

varmais commented Jun 1, 2016

I'm running my tests with mocha and found mocha-image-compiler useful when testing components which require images. See my example repository for examples: react-native-unit-tests.

It comes with downside though: You will get a prop type warning when running the tests, but I'll rather take the warning than leave components untested.

@RealOrangeOne
Copy link
Owner

RealOrangeOne commented Jun 1, 2016

@varmais Wow, that's really nice! Although yes the prop warning would get annoying. It's definately much easier than using mockery! We could fork that library and add it in, conditionally obviously.

@zhaotai
Copy link

zhaotai commented Jul 5, 2016

Well, the solution of @miracle2k can not solve my problem directly because the image name they mock is just the same to the actual image name.

But as we all known, if you require an image without 2x or 3x postfix just like abc.png instead of abc@2x.png, react-native will also require the right one automatically depending on the active device. And that's why I confronted this problem and all above didn't solve it.

So inspired by the code of @miracle2k , I replaced the file with request directly because in my situation there is no abc.png at all and the function _resolveFilename will throw a can not find module error.
At last, my code is below

const m = require('module');
const originalLoader = m._load;

m._load = function hookedLoader(request, parent, isMain) {
  if (request.match(/.jpeg|.jpg|.png$/)) {
    return { uri: request };
  }

  return originalLoader(request, parent, isMain);
};

I haven't tried the mockery solution because I use mocha and enzyme as my unit test library and I have a setup.js file to handle all the mocks whatever the native modules or third-party modules. And the best solution for me is to write these code in the same file.

@ryyppy
Copy link

ryyppy commented Jul 5, 2016

Funny that this discussion just arised.. I was working on the same mocking problem independently yesterday and came to a very similar (non-working) solution as @zhaotai ... this will save me many hours of debugging the node module system... it's also not as intrusive as mockery.

I know mockery and I used it for other products myself.. the thing is, before I would ever use it, I might as well just use jest instead ... because jest does practically the exact same thing more efficiently and with first level React-Native support...

@GantMan
Copy link
Author

GantMan commented Jul 5, 2016

Just tested the @zhaotai and @miracle2k solution. Works fantastic. Going to be moving this into our default AVA tests for the Ignite project. Now everyone who creates a new Ignite starter will have their work right away. I'll be sure to make more shout outs in the release notes. Thanks!

@GantMan
Copy link
Author

GantMan commented Jul 5, 2016

O and I"m closing this ticket :) cheers!

@GantMan GantMan closed this as completed Jul 5, 2016
@icynoangel
Copy link

Hmm... I have added the hookLoader for images in my mocha setup file, I don't get the error about images anymore, but I do have the following error:

TypeError: Plugin 1 specified in "/node_modules/react-native-router-flux/node_modules/react-native-experimental-navigation/package.json" was expected to return a function but returned "undefined"

Anyone got this error?

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests