Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
317 views
in Technique[技术] by (71.8m points)

typescript - Error: "Router must be defined in href" stenciljs router-v2 testing

I am trying to test a stencil component. I use "stencil-router-v2". I have a link in my component's render method with "href" function: <a {...href(`/news/${this.page}`)} class="btn btn--primary btn--medium btn--view">View</a> When I try to test it, I see the next error: "Router must be defined in href". Can you please explain to me how to test the "href" function correctly? What test code do I need to write in order for the test to pass?

My component code:

import { Component, h, Prop, Event, EventEmitter } from '@stencil/core';
import { href } from 'stencil-router-v2';
import state from '../store/store';

@Component({
  tag: 'my-news-page',
  styleUrl: 'my-news-page.scss'
})

export class MyNewsPage {  
  @Prop() page: string;
  @Prop() news;

  @Event() newsDeleted: EventEmitter;

  deleteNewsHandler = id => {
    this.newsDeleted.emit(id);
  }

  componentWillRender() {
    this.news = state.newsList.find(news => news.id === +this.page);
  }

  render() {
    return (
      <section>
        <h3>{this.news.title}</h3>
        <p>{this.news.body}</p>
        <button class="btn btn--primary btn--medium btn--delete" onClick={() => this.deleteNewsHandler(this.news.id)}>Delete</button>
        <a {...href(`/news/${this.page}`)} class="btn btn--primary btn--medium btn--view">View</a>
      </section>
    )
  }
}

My test code:

import { newSpecPage } from '@stencil/core/testing';
import { MyNewsPage } from '../my-news-page';
import state from '../../store/store';

state.newsList = [
  { id: 1, title: 'title1', body: 'content1' },
  { id: 2, title: 'title2', body: 'content2' },
  { id: 3, title: 'title3', body: 'content3' }
];

const ID = '1';

describe('my-news-page', () => {
  it('should display remaining items', () => {
    let page = new MyNewsPage();
    page.news = state.newsList;
    expect(page.news.length).toEqual(3);
  });

  it('renders', async () => {
    const page = await newSpecPage({
      components: [MyNewsPage],
      html: '<div></div>'
    });
    let cmp = page.doc.createElement('my-news-page');
    (cmp as any).page = ID;
    (cmp as any).news = state.newsList.find(news => news.id === +cmp.page);
    page.root.appendChild(cmp);
    await page.waitForChanges();
    expect(page.root).toMatchSnapshot();
  });
});

Terminal:

  my-news-page
    ? should display remaining items (7 ms)
    ? renders (37 ms)

  ● my-news-page ? renders

    Router must be defined in href

      28 |         <p>{this.news.body}</p>
      29 |         <button class="btn btn--primary btn--medium btn--delete" onClick={() => this.deleteNewsHandler(this.news.id)}>Delete</button>
    > 30 |         {<a {...href(`/news/${this.page}`)} class="btn btn--primary btn--medium btn--view">View</a>}
         |                 ^
      31 |       </section>
      32 |     )
      33 |   }

      at Object.href (node_modules/stencil-router-v2/dist/index.js:106:15)
      at MyNewsPage.render (src/components/my-news-page/my-news-page.tsx:30:17)
      at callRender (node_modules/@stencil/core/internal/testing/index.js:421:46)
      at updateComponent (node_modules/@stencil/core/internal/testing/index.js:402:247)
      at node_modules/@stencil/core/internal/testing/index.js:397:22
      at then (node_modules/@stencil/core/internal/testing/index.js:459:47)
      at dispatchHooks (node_modules/@stencil/core/internal/testing/index.js:397:7)
      at a (node_modules/@stencil/core/internal/testing/index.js:387:18)
      at a (node_modules/@stencil/core/internal/testing/index.js:25:17)```

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I think you might be able to create a router, then use the template option for the spec page (you'll also have to change the test file's extension to .tsx and import h):

import { Fragment, h, newSpecPage } from '@stencil/core/testing';
import { createRouter } from 'stencil-router-v2';

const Router = createRouter();

// ...

describe('my-news-page', () => {
  it('renders', async () => {
    const page = await newSpecPage({
      components: [MyNewsPage],
      template: () => (
        <Fragment>
          <Router>
            {/* You might have to define routes here, so probably
                best to import your app's router instead. */}
          </Router>

          <my-news-page
            page={ID}
            news={state.newsList.find(news +> news.id === ID)
          />
        </Fragment>
      ),
    });

    await page.waitForChanges();
    expect(page.root).toMatchSnapshot();
  });
});

Using template also simplifies setting your props etc. Be aware that this needs to be a function returning JSX. Fragment is only available in newer Stencil versions afaik.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...