Cross-framework web components library 📚 using Lit (Part II)
Tags: typescript, webcomponents, webdev, programming
Published: 2022-08-31 13:27:46

##Testing
After we conclude our card component is time to test it. I have used the testing package from Open Web Components, and you can find the documentation [here](https://open-wc.org/docs/testing/testing-package/) in conjunction with jasmine and karma.

We start describing what component will be tested and defining what we will mock as component properties:

```javascript
import { Card } from './card';

describe('Card Component', () => {
	let el: Card;

	const card = {
		altText: 'Je suis a image',
		ctaText: 'Click me',
		image: 'http://example.com/image/',
		link: 'http://example.com/link/',
		text: 'amazing text',
		textDesc: 'other amazing text for test',
		textDescLink: 'http://example.com/author/',
		title: 'amazing title',
	};

}

```

After that, before the test runs, we boot a fixture that will pass a `Card` component with our  custom configuration, and we check if the element is defined, checking the `instanceOf`:

```javascript
...

import { assert, expect, fixture, html } from '@open-wc/testing';

describe('Card Component', () => {
	...
	
	beforeEach(async () => {
   		el = await fixture<Card>(html` <card-image .card=${card}></card-image> `);
  	});

	it('is defined', () => {
	    assert.instanceOf(el, Card);
	});

}

```

If everything is well configured, we run the test, and we should have one successful test:

![Testing console](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bh6eeal7ndx4k8fe1b38.png)

Let's have a bit more coverage on that component:

```javascript
...

describe('Card Component', () => {
  	  ...

	  it('renders the image', () => {
	    const image = el.shadowRoot?.querySelector('img');
	    expect(image).to.exist;
	    expect(image?.alt).to.equal(card.altText);
	    expect(image?.src).to.equal(card.image);
	  });
	
	  it('renders the title', () => {
	    const title = el.shadowRoot?.querySelector('#card-link');
	    expect(title).to.exist;
	    expect(title?.getAttribute('href')).to.equal(card.link);
	    expect(title?.textContent).to.equal(card.title);
	  });
	
	  it('renders the text', () => {
	    const text = el.shadowRoot?.querySelector('p');
	    expect(text).to.exist;
	    expect(text?.textContent).to.equal(card.text);
	  });
	
	  it('renders the cta text', () => {
	    const ctaText = el.shadowRoot?.querySelector('.cta a');
	    expect(ctaText).to.exist;
	    expect(ctaText?.textContent).to.equal(card.ctaText);
	  });
	
	  it('renders the description link', () => {
	    const textDesc = el.shadowRoot?.querySelector('small a');
	    expect(textDesc).to.exist;
	    expect(textDesc?.getAttribute('href')).to.equal(card.textDescLink);
	    expect(textDesc?.textContent).to.equal(card.textDesc);
	  });
	
	  it('dispatch the mousedown event', () => {
	    el.mouseDown();
	    expect(el.down).to.be.a('number');
	  });

}

```

This should leverage the coverage to more than 80%.<br/><br/>

##Cards component
We will be doing a second component where we can pass the configuration to have multiple cards.
This exposes the ability to reuse one internal component and do another with other purposes that can have a different configuration. Also, we will be using lit [directives](https://lit.dev/docs/templates/directives/).

We start by doing a new component at the same level as `card.ts`:

```javascript
...

import { LitElement } from 'lit';
import { customElement } from 'lit/decorators.js';

@customElement('cards-images')
export class Cards extends LitElement {}

```

After we define the external `@property`, that basically will receive an array of `CardConfig` objects:

```javascript
...

import { customElement, property } from 'lit/decorators.js';

@customElement('cards-images')
export class Cards extends LitElement {
	
	@property({ type: Array }) cards!: CardConfig[];

}

```

We can now render the cards container and use the `repeat` directive to generate various instances of the card based on the configuration we passed in the `@property`:

```javascript
...

import { html, LitElement } from 'lit';
import { repeat } from 'lit/directives/repeat.js';

@customElement('cards-images')
export class Cards extends LitElement {
	
	...
	
	render() {
    return html` <div class="cards">
      <ul>
        ${repeat(this.cards, (current) => html` <card-image .card=${current}></card-image> `)}
      </ul>
    </div>`;
  }

}

```

To finalize, we only need to add the component styles and the shared ones:

```javascript
...

import { css, html, LitElement } from 'lit';
import { sharedStyles } from './shared/style';

const componentStyle = css`
  .cards > ul {
    list-style: none;
    margin: 0;
    padding: 0;
  }

  @supports (display: grid) {
    .cards > ul {
      display: grid;
      grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
      grid-column-gap: 1.5rem;
      grid-row-gap: 1.5rem;
    }
  }
`;


@customElement('cards-images')
export class Cards extends LitElement {

  static styles = [sharedStyles, componentStyle];
	
	...

}

```

And now, you should have a component that will render like this:

![Web component cards](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b2bkib4bvmul09u2fpln.png)<br/><br/>

In the next part, we will cover distributing the lib via a node package using the npm registry and how to use the components in different frameworks/libs environments.