官术网_书友最值得收藏!

Providing options to a dropdown

The Git tag for this section is providing-options.

The first option in our dropdown should be an empty value. This is the value that's initially selected when the user creates a new appointment: no option is selected. Let's write that test now:

it('initially has a blank value chosen', () => {
render(<AppointmentForm />);
const firstNode = field('service').childNodes[0];
expect(firstNode.value).toEqual('');
expect(firstNode.selected).toBeTruthy();
});

Make that pass by adding in an empty option into the top of the select:

export const AppointmentForm = () => (
<form id="appointment">
<select name="service">
<option />
</select>
</form>
);

In the first chapter, you saw the importance of keeping test data simple. Our tests reference just what we need to exercise the test, and nothing more. We have to be very careful of noise, and we aim to keep our tests as concise as possible.

Now, we'll apply that principle to a larger set of data.

Our salon provides a whole range of services and we'd like to ensure that they are all listed in the app. We could start our test by defining our expectations like this:

it('lists all salon services', () => {
const selectableServices = [
'Cut',
'Blow-dry',
'Cut & color',
'Beard trim',
'Cut & beard trim',
'Extensions' ];

As it turns out, there's a simpler way. We want to prove that our code can take an array and list each array item within the dropdown. We can do this with just two items in our array. Any more is overkill.

But how do we use only two items in our test when we need six items for the production code?

We do that by passing in our array as a prop to the component, rather than hard-coding it within the component itself. We can then provide a two-item array for our tests and the full list when we hook the AppointmentForm up in our application entrypoint.

Add the following test:


it('lists all salon services', () => {
const selectableServices = ['Cut', 'Blow-dry'];

render(
<AppointmentForm
selectableServices={selectableServices}
/>
);

const optionNodes = Array.from(
field('service').childNodes
);
const renderedServices = optionNodes.map(
node => node.textContent
);
expect(renderedServices).toEqual(
expect.arrayContaining(selectableServices)
);
});

The expectations here are more complicated than we've seen before. The Array.from method takes childNodes, which is a NodeList, and produces a standard JavaScript array with the same nodes. We then use the Array.map function to pull out the textContent of these nodes and check that it matches our original array.

NodeList objects are "live" in that they automatically update when the DOM changes. By calling Array.from, we are taking a snapshot of the values within it at a particular moment in time.

The toEqual matcher, when applied to arrays, will check that each array has the same number of elements and that each element appears in the same place.

I've used "real"-like data for my expected services: cut and blow-dry. It's also fine to use non-real names such as Service A and Service B. Often, that can be more descriptive. Both are valid approaches.

Let's make this pass. Change the component definition, as follows:

export const AppointmentForm = ({ selectableServices }) => (
<form id="appointment">
<select name="service">
<option />
{selectableServices.map(s => (
<option key={s}>{s}</option>
))}

</select>
</form>
);

The latest test should now pass, but our earlier tests break because of the introduction of the new prop. We could update our tests to explicitly pass a selectableServices prop into AppointmentForm. We could also change our production code to use a default array if selectableServices isn't defined.

But there's another way, which is also conveniently how we'll get our real data into the application.

主站蜘蛛池模板: 中方县| 重庆市| 浦江县| 巨鹿县| 长子县| 石屏县| 乐安县| 宁安市| 呼图壁县| 富蕴县| 南岸区| 绥棱县| 永靖县| 三门峡市| 宁都县| 通道| 阜宁县| 乌兰浩特市| 高淳县| 东莞市| 尼勒克县| 车致| 新邵县| 台湾省| 五家渠市| 商都县| 诏安县| 佛冈县| 漯河市| 鲜城| 靖西县| 乌兰浩特市| 丁青县| 东源县| 惠来县| 英德市| 鄢陵县| 太仆寺旗| 孝感市| 广宁县| 平武县|