87

I'm unit testing one of my directives (angularjs) using grunt/karma/phantomjs/jasmine. My tests run fine

describe('bar foo', function () {
    beforeEach(inject(function ($rootScope, $compile) {
        elm = angular.element('<img bar-foo src="img1.png"/>');
        scope = $rootScope.$new();
        $compile(elm)();
        scope.$digest();
    }));
    ....
});

but I do get these 404s

WARN [web-server]: 404: /img1.png
WARN [web-server]: 404: /img2.png
...

Although they do nothing, they do add noise to the log output. Is there a way to fix this ? (without changing karma's logLevel of course, because I do want to see them)

Jeanluca Scaljeri
  • 19,619
  • 37
  • 147
  • 259

7 Answers7

109

That is because you need to configurate karma to load then serve them when requested ;)

In your karma.conf.js file you should already have defined files and/or patterns like :

// list of files / patterns to load in the browser
files : [
  {pattern: 'app/lib/angular.js', watched: true, included: true, served: true},
  {pattern: 'app/lib/angular-*.js', watched: true, included: true, served: true},
  {pattern: 'app/lib/**/*.js', watched: true, included: true, served: true},
  {pattern: 'app/js/**/*.js', watched: true, included: true, served: true},
  // add the line below with the correct path pattern for your case
  {pattern: 'path/to/**/*.png', watched: false, included: false, served: true},
  // important: notice that "included" must be false to avoid errors
  // otherwise Karma will include them as scripts
  {pattern: 'test/lib/**/*.js', watched: true, included: true, served: true},
  {pattern: 'test/unit/**/*.js', watched: true, included: true, served: true},
],

// list of files to exclude
exclude: [

],

// ...

You can have a look here for more info :)

EDIT : If you use a nodejs web-server to run your app, you can add this to karma.conf.js :

proxies: {
  '/path/to/img/': 'http://localhost:8000/path/to/img/'
},

EDIT2 : If you don't use or want to use another server you can define a local proxy but as Karma doesn't provide access to port in use, dynamically, if karma starts on another port than 9876 (default), you will still get those annoying 404...

proxies =  {
  '/images/': '/base/images/'
};

Related issue : https://github.com/karma-runner/karma/issues/872

glepretre
  • 8,365
  • 5
  • 41
  • 56
  • 4
    In my case these images do not exist. The solution you provide assumes the files exist, right ? – Jeanluca Scaljeri Jan 21 '14 at 19:09
  • Yes of course! I think I misunderstood, it makes sense to have 404 errors for non-existing files, right? You would like to hide warnings related to images? Without changing the log level, I see no solution, moreover, that would hide other warnings, which would be risky. Why not create empty .png files in "test/img" folder for example? :) – glepretre Jan 22 '14 at 11:10
  • For some reason I can't get it to work. What exactly is the relation between the url used in the HTML and the pattern in karma.conf.js ? For example if I have an image in **test/assets/img.png**, what should be the url ? – Jeanluca Scaljeri Jan 24 '14 at 12:48
  • 1
    My apologies, this solution should work but I kept getting 404 errors too. I believe this is related to a lack of implementation in Karma and I am surprised that we are the only one to get it. I found a (bit hacky) way to get this to work but you will need to run another (web)server in parallel of Karma. I will edit my answer. ;) – glepretre Jan 27 '14 at 08:34
  • If the `basePath` option isn't used, the images could also be served under the `absolute/` path – Atav32 Dec 08 '14 at 19:11
  • 3
    In response to EDIT2 if you run karma on a custom port you can avoid the 404s by linking to the full URI of the karma server: (assuming `port: 9999`) ```proxies = { '/images/': 'http://localhost:9999/base/images/' };``` – Josh Jan 31 '15 at 00:24
  • Tip: make sure to include the forward slash in the proxies object: `'/images/': '/base/images/'`. Omitting it was giving me a hard time... – Atif Dec 21 '16 at 21:22
  • 1
    I found that `'/images/': ''` is actually sufficient enough to suppress the warning, and doesn't require setting up files. – Tezra Jul 15 '20 at 16:43
18

The confusing piece of the puzzle for me was the 'base' virtual folder. If you don't know that needs to be included in the asset paths of your fixtures you will find it hard to debug.

As-per the configuration documentation

By default all assets are served at http://localhost:[PORT]/base/

Note: this may not be true for other versions - I'm on 0.12.14 and it worked for me but the 0.10 docs dont mention it.

After specifying the files pattern:

{ pattern: 'Test/images/*.gif', watched: false, included: false, served: true, nocache: false },

I could use this in my fixture:

<img src="base/Test/images/myimage.gif" />

And I didn't need the proxy at that point.

Alex
  • 7,405
  • 5
  • 46
  • 72
Tom Elmore
  • 1,882
  • 14
  • 20
  • This is the clincher. The top answer explains this but very briefly - thanks for expanding. – jlb Apr 21 '16 at 16:11
10

You can create generic middleware inside your karma.conf.js - bit over the top but did the job for me

First define dummy 1px images (I've used base64):

const DUMMIES = {
  png: {
    base64: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
    type: 'image/png'
  },
  jpg: {
    base64: 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAABAAEDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD3+iiigD//2Q==',
    type: 'image/jpeg'
  },
  gif: {
    base64: 'data:image/gif;base64,R0lGODlhAQABAAAAACwAAAAAAQABAAA=',
    type: 'image/gif'
  }
};

Then define middleware function:

function surpassImage404sMiddleware(req, res, next) {
  const imageExt = req.url.split('.').pop();
  const dummy = DUMMIES[imageExt];

  if (dummy) {
    // Table of files to ignore
    const imgPaths = ['/another-cat-image.png'];
    const isFakeImage = imgPaths.indexOf(req.url) !== -1;

    // URL to ignore
    const isCMSImage = req.url.indexOf('/cms/images/') !== -1;

    if (isFakeImage || isCMSImage) {
      const img = Buffer.from(dummy.base64, 'base64');
      res.writeHead(200, {
        'Content-Type': dummy.type,
        'Content-Length': img.length
      });
      return res.end(img);
    }
  }
  next();
}

Apply middleware in your karma conf

{
    basePath: '',
    frameworks: ['jasmine', '@angular/cli'],
    middleware: ['surpassImage404sMiddleware'],
    plugins: [
      ...
      {'middleware:surpassImage404sMiddleware': ['value', surpassImage404sMiddleware]}
    ],
    ...
}
thorn0
  • 6,927
  • 2
  • 53
  • 86
Jakub Żwirko
  • 97
  • 1
  • 5
  • Did you ever make it into an actual package? I would love to just npm install this – Akxe Aug 28 '19 at 14:33
  • If you want to suppress all image requests, just check `req.headers.accept` to see if it contains `image` and return 204 if it does. – cleong Jan 07 '20 at 21:27
9

Based on @glepretre's answer, I've created an empty .png file and added this to the config to hide 404 warnings:

proxies: {
  '/img/generic.png': 'test/assets/img/generic.png'
}
the_karel
  • 1,274
  • 11
  • 14
4

To fix, in your karma.conf.js make sure to point to the served file with your proxies:

files: [
  { pattern: './src/img/fake.jpg', watched: false, included: false, served: true },
],
proxies: {
  '/image.jpg': '/base/src/img/fake.jpg',
  '/fake-avatar': '/base/src/img/fake.jpg',
  '/folder/0x500.jpg': '/base/src/img/fake.jpg',
  '/undefined': '/base/src/img/fake.jpg'
}
Boris Yakubchik
  • 2,387
  • 1
  • 24
  • 30
4

Even though its an old thread, it took me a couple hours to actually get my image to actually be served from karma to eliminate the 404. The comments were just not thorough enough. I believe I can clarify the solution with this screenshot. Essentially the one thing that many comments were missing is the fact that the proxy value must start with "/base", even though base is not in any of my folder pathing, nor is it in my requests.

("base" without the forward slash resulted in karma returning a 400 BAD REQUEST)

Now after running ng test, I can successfully serve "./src/assets/favicon.png" from the url: http://localhost:9876/test/dummy.png

In my project I am using the following npm package versions:

  • karma v4.3.0
  • jasmine-core v3.2.1
  • karma-jasmine v1.1.2
  • @angular/cli v8.3.5
  • angular v8.2.7

VSCode project structure with karma.conf.js assets locations

Jeff
  • 386
  • 3
  • 7
  • This insight was particularly helpful for me to understand that there's a difference (from Karma's perspective) for serving files within the base area, and from outside the base area (which was ultimately my issue), which then led me to https://github.com/karma-runner/karma/issues/2703. So, thank you for this clarification. – dpmott Aug 24 '20 at 16:32
2

If you have root path somewhere in your configuration file you can also use something like this:

proxies: {
  '/bower_components/': config.root + '/client/bower_components/'
}
Gucu112
  • 511
  • 4
  • 7