Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TypeError: attributes.item is not a function #1886

Closed
lukaszkups opened this issue Jul 23, 2021 · 9 comments
Closed

TypeError: attributes.item is not a function #1886

lukaszkups opened this issue Jul 23, 2021 · 9 comments

Comments

@lukaszkups
Copy link

lukaszkups commented Jul 23, 2021

Subject of the issue

I have a component that contains an image (<img />) element.
When I execute a test with the following code:

const img = wrapper.find('img') // img element exists for 100%
img.trigger('load') // <- here error happens / error log points for this particular line

Steps to reproduce

Create any Vue component that contains img element inside with binded @load="onImgLoad" method for img DOM element.

Create following test:

test('Should call onLoad after img load', async () => {
    const onImgLoad = jest.fn();
    const wrapper: Wrapper<any> = shallowMount(ImageComponent, {
      methods: {
        onImgLoad,
      },
    });

    const img = wrapper.find('img');
    await img.trigger('load');
    expect(onImgLoad).toHaveBeenCalledTimes(1);
  });

Expected behaviour

Test should pass just fine, load event should be triggered 1 time.

Actual behaviour

Test fails and throws an error:

TypeError: attributes.item is not a function

It also fails for other DOM elements (selected via wrapper.find()), when I try to use .trigger methods with other events on them.

Node version: v14.15.4

Vue-test-utils version: 1.0.0-beta.31

Using Vue 2 + Typescript

Possible Solution

I've updated the vue-test-utils.js file inside my node_modules folder (file url in the github repo: https://github.com/vuejs/vue-test-utils/blob/dev/packages/test-utils/src/wrapper.js )

In this method definition:

/**
 * Returns an Object containing all the attribute/value pairs on the element.
 */
attributes(key?: string): { [name: string]: string } | string {
  this.__warnIfDestroyed()

  const attributes = this.element.attributes
  const attributeMap = {}
  for (let i = 0; i < attributes.length; i++) {
    const att = attributes.item(i)
    attributeMap[att.localName] = att.value
  }

    return key ? attributeMap[key] : attributeMap
  }

I've replaced const att = attributes.item(i) to const att = attributes[i] and test started passing just fine.

@standbyoneself
Copy link
Contributor

I have tried to run a similar test and it works both 1.0.3 and 1.0.0-beta.31 VTU versions:

import { shallowMount } from '@vue/test-utils';
import MyImage from '@/components/MyImage';

describe('MyImage.vue', () => {
  it('calls an `imgLoadCallback()`', async () => {
    const wrapper = shallowMount(MyImage);

    const spy = jest.spyOn(wrapper.vm, 'imgLoadCallback');

    const img = wrapper.find('img');

    await img.trigger('load');

    expect(spy).toHaveBeenCalled();
  });
});
<template>
  <img :src="logo" @load="imgLoadCallback()" />
</template>

<script>
import logo from '../assets/logo.png';

export default {
  name: 'MyImage',
  data() {
    return {
      logo,
    };
  },
  methods: {
    imgLoadCallback() {
      console.log("I'm loaded!");
    },
  },
};
</script>

@nikhilgosavi007
Copy link

nikhilgosavi007 commented Sep 21, 2021

Subject of the issue

I have a simple component with a form and a button element. When I execute a test with the following code:

 const btn = wrapper.find('[type=submit]');
 await btn.trigger('click');

I get the following error:

TypeError: attributes.item is not a function

Project Configuration

Node version: v14.15.4

Vue-test-utils version: 1.0.3

Using Vue 2 + Typescript

Steps to reproduce

My Component

<template>
  <div id="app">
    <form id="my-form" @submit.prevent="submitClicked">
      <button type="submit">Submit</button>
    </form>
  </div>
</template>

<script lang="ts">
import { Component, Vue } from "vue-property-decorator";

@Component({})
export default class App extends Vue {
  mounted(): void {
    console.log("APP loaded");
  }

  submitClicked(): void {
    console.log("form was submitted !");
  }
}
</script>

My test

import { shallowMount, Wrapper } from "@vue/test-utils";
import App from "./App.vue";

describe("App.vue", () => {
  it("calls submitClicked", async () => {
    const submitClicked = jest.fn();
    const wrapper: Wrapper<App> = shallowMount(App, {
        methods: {
            submitClicked,
        },
    });

    const btn = wrapper.find('[type=submit]');
    await btn.trigger('click');
     expect(submitClicked).toHaveBeenCalledTimes(1);
  });
});

My jest.config.js

module.exports = {
  preset: '@vue/cli-plugin-unit-jest/presets/typescript-and-babel',
  coverageReporters: ['text', 'json-summary', 'lcov'],
  transformIgnorePatterns: [],
  testEnvironment: '@happy-dom/jest-environment',
  coverageThreshold: {
    global: {
      branches: 90,
      functions: 90,
      lines: 90,
      statements: 90,
    },
  },
  testMatch: ['**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)'],
};

The error is being triggered from the vue-test-utils.js file inside my node_modules folder as pointed out by @lukaszkups

I can confirm that replacing const att = attributes.item(i) to const att = attributes[i] worked.

Were you able to find any workaround @lukaszkups ?

@lukaszkups
Copy link
Author

@nikhilgosavi007 unfortunately not, sorry, I've just abandoned testing that particular use case and switched to different topics to be honest 🥲

@nikhilgosavi007
Copy link

@lukaszkups Try using jsdom instead of @happy-dom/jest-environment as your testEnvironment. It worked for me :)

@lukaszkups
Copy link
Author

@lukaszkups Try using jsdom instead of @happy-dom/jest-environment as your testEnvironment. It worked for me :)

Unfortunately I'm not allowed to change this dependency, but thank you for the tip with available solution 🙌

@bjufre
Copy link

bjufre commented Aug 10, 2022

@lukaszkups How did you end up fixing it?

@lukaszkups
Copy link
Author

I didn't, just skipped testing this particular piece of code for the time (and it still is not tested) - I've added a possible suggestion how to solve it (by modifying const att = attributes.item(i) to const att = attributes[i]) in my original post message but don't know if it has been already fixed in newer versions or not.

@IGx89
Copy link
Contributor

IGx89 commented Oct 7, 2022

This is the root cause: capricorn86/happy-dom#308. Not fixed yet, but happy-dom has been having a lot of activity lately so maybe soon...

@Demivan
Copy link

Demivan commented Oct 21, 2022

capricorn86/happy-dom#308 is fixed now in 7.6.0

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

No branches or pull requests

7 participants