Categories
jQuery

Getting Started with jQuery

jQuery is a popular JavaScript for creating dynamic web pages.

In this article, we’ll look at how to using jQuery in our web apps.

Adding jQuery

We can add jQuery by adding a script tag:

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>

We can also install the jquery package by running:

npm install jquery

with NPM or:

yarn add jquery

Also, we can install it with Bower:

bower install jquery

After installing it, we can start using it.

.add()

The .add() method lets us get a child element from a parent.

For example, if we have:

<p>
  <div>
    foo
  </div>
</p>

Then we call:

$("p").add("div").addClass("widget");

to get the div that’s inside the p element.

Then we call addClass to add a class to the div inside the p element.

.addBack()

The .addBack() method lets us get the child elements from the parent element.

For example, if we have the following HTML:

<div class="after-addback">
  <p>First Paragraph</p>
  <p>Second Paragraph</p>
</div>

and the following CSS:

.background {
  background: yellow;
}

Then we get the p elements in the div .

Then we use addBack to get the div again.

And finally, we call addClass to add the background class.

.addClass()

The .addClass() method lets us add one or more classes to an element.

For example, we can write:

$("p").addClass("myClass yourClass");

to add the myClass and yourClass classes to the p element.

.after()

The .after() method inserts content after each element in the set of matched elements.

For example, if we have the following HTML:

<div class="container">
  <h2>Greetings</h2>
  <div class="inner">Hello</div>
  <div class="inner">Goodbye</div>
</div>

Then we get the item with the inner class and add the HTML TO it by writing:

$(".inner").after("<p>Test</p>");

.ajaxComplete()

The .ajaxComplete() lets us add a callback that’s called when the Ajax request is completed.

For example, we can write:

$(document).ajaxComplete(function(event, xhr, settings) {
  console.log(event, xhr, settings)
});

$.ajax({
  url: "https://yesno.wtf/api",
  context: document.body
}).done(function() {
  $(this).addClass("done");
});

We called ajaxComplete with the callback that’s called when we make a request with $.ajax .

Then we call $.ajax to make the Ajax request.

settings has the url property with the request URL.

The settings.type property has the request method name.

xhr has the XHR object. The readyState property has the ready state.

.ajaxError()

The .ajaxError() method lets us add a callback that’s called when there’s an error.

For example, we can use it by writing:

$(document).ajaxError(function(event, xhr, settings, thrownError) {
  console.log(event, xhr, settings, thrownError)
});

$.ajax({
  url: "bad-url",
  context: document.body
}).done(function() {
  $(this).addClass("done");
});

We have an invalid URL set as the value of the url property.

Then the ajaxError callback will be called.

It has the thrownError parameter which isn’t in the ajaxComplete callback.

.ajaxSend()

The ajaxSend method lets us add a callback that’s called when the request is sent.

For example, we can write:

$(document).ajaxSend(function(event, xhr, settings) {
  console.log(event, xhr, settings)
});

$.ajax({
  url: "https://yesno.wtf/api",
  context: document.body
}).done(function() {
  $(this).addClass("done");
});

We have the same callback as the other ajax methods.

And the content is the same.

Conclusion

We can add jQuery and call its methods Ajax methods to listen to HTTP requests.

Also, we can use the add and addBack methods to get elements.

Categories
JavaScript Best Practices

Object-Oriented JavaScript — Objects and Constructors

JavaScript is partly an object-oriented language.

To learn JavaScript, we got to learn the object-oriented parts of JavaScript.

In this article, we’ll look at objects and constructors.

Accessing an Object’s Properties

We can access an object’s properties by using the square bracket notation or the dot notation.

To use the square bracket notation, we can write:

dog['name']

This works for all property names, whether they’re valid identifiers or not.

We can also use them to access properties by passing in a property name dynamically with a string or symbol.

The dot notation can be used by writing:

dog.name

This is shorter but only works with valid identifiers.

An object can contain another object.

For instance, we can write:

const book = {
  name: 'javascript basics',
  published: 2020,
  author: {
    firstName: 'jane',
    lastName: 'smith'
  }
};

The author property has the firstName and lastName properties.

We can get nested properties by writing:

book['author']['firstName']

or

book.author.firstName

We can mix the square brackets and dot notation.

So we can write:

book['author'].firstName

Calling an Object’s Methods

We can call a method the same way we call any other function.

For instance, if we have the following object:

const dog = {
  name: 'james',
  gender: 'male',
  speak() {
    console.log('woof');
  }
};

Then we can call the speak method by writing:

dog.speak()

Altering Properties/Methods

We can change properties by assigning a value.

For instance, we can write:

dog.name = 'jane';
dog.gender = 'female';
dog.speak = function() {
  console.log('she barked');
}

We can delete a property from an object with the delete operator:

delete dog.name

Then when we try to get dog.name , we get undefined .

Using this Value

An object has its own this value.

We can use it in our object’s methods.

For instance, we can write:

const dog = {
  name: 'james',
  sayName() {
    return this.name;
  }
};

We return this.name , which should be 'james' .

Because this is the dog object within the sayName method.

Constructor Functions

We can create constructor functions to let us create objects with a fixed structure.

For instance, we can write:

function Person(name, occupation) {
  this.name = name;
  this.occupation = occupation;
  this.whoAreYou = function() {
    return `${this.name} ${this.occupation}`
  };
}

We have the instance properties name , occupation and the this.whoAreYou instance method.

They’re all returned when we create a new object with the constructor.

Then we can use the new operator to create a new Person instance:

const jane = new Person('jane', 'writer');

The value of this os set to the returned Person instance.

We should capitalize the first letter of the constructor so that we can tell them apart from other functions.

We shouldn’t call constructor functions without the new operator.

So we don’t write:

const jane = Person('jane', 'writer');

The value of this won’t be set to the returned Person instance this way.

The Global Object

The global object in the browser is the window object.

We can add properties to it with var at the top level:

var a = 1;

Then window.a would be 1.

Calling a constructor without new will return undefined .

So if we have:

const jane = Person('jane', 'writer');

then jane is undefined .

The built-in global functions are properties of the global object.

So parseInt is the same as window.parseInt .

Conclusion

We can access object properties in 2 ways.

Also, we can create constructor functions to create objects with a set structure.

Categories
React

React Testing — Getting Started

Automated tests are important for most apps.

In this article, we’ll take a look at how to write tests for React components.

Testing Tools

Create React App has test files and scripts built into the project.

First Test

We can write our first test by adding test files in our Create React App project.

If we have App.js :

import React from 'react';
import logo from './logo.svg';
import './App.css';

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    </div>
  );
}

export default App;

In App.test.js , we write:

import React from 'react';
import { render } from '@testing-library/react';
import App from './App';

test('renders learn react link', () => {
  const { getByText } = render(<App />);
  const linkElement = getByText(/learn react/i);
  expect(linkElement).toBeInTheDocument();
});

to add the test.

The render function renders the App component that we imported.

Then we call getByText with a regex to get the element we’re looking for.

Finally, we call toBeInTheDocument to check linkElement is there.

Data Fetching

We add setup and teardown code with React tests.

Also, we can mock any HTTP requests in our test code so that we can run our tests in isolation.

For example, if we have the following component:

Answer.js

import React, { useState, useEffect } from "react";

export default function Answer(props) {
  const [data, setData] = useState({});

  async function fetchData() {
    const response = await fetch("https://yesno.wtf/api");
    setData(await response.json());
  }

  useEffect(() => {
    fetchData(props.id);
  }, []);

  return (
    <div>
      <p>{data.answer}</p>
    </div>
  );
}

Then we have to mock the Fetch API.

To do that, we can write:

Answer.test.js

import React from "react";
import { render, unmountComponentAtNode } from "react-dom";
import { act } from "react-dom/test-utils";
import Answer from "./Answer";

let container = null;
beforeEach(() => {
  container = document.createElement("div");
  document.body.appendChild(container);
});

afterEach(() => {
  unmountComponentAtNode(container);
  container.remove();
  container = null;
});

it("renders answer data", async () => {
  const fakeData = {
    answer: "yes",
    forced: false,
    image: "https://yesno.wtf/assets/yes/6-304e564038051dab8a5aa43156cdc20d.gif"
  };
  jest.spyOn(global, "fetch").mockImplementation(() =>
    Promise.resolve({
      json: () => Promise.resolve(fakeData)
    })
  );

  await act(async () => {
    render(<Answer />, container);
  });

  expect(container.querySelector("p").textContent)
    .toBe(fakeData.answer);
  global.fetch.mockRestore();
});

In the code above, we have the container which we use to mount our component.

We have the beforeEach hook to create the container element to mount our component in.

And we have the afterEach hook to unmount our component and remove the container.

Then in our 'render answer data' test, we create the fakeData object with our mock response data.

Then we mock the fetch function in Answer.js by using the jest.spyOn method.

global is the global object, 'fetch' is the fetch function.

mockImplementation lets us mock fetch with an object with the json method that’s set to a function that returns a promise that resolves to the fake data.

Then we mount our Answer component with act and await it to let the promise resolve.

And then we check what’s in the container ‘s p element to check if it has what we expect.

Then finally, we call global.fetch.mockRestore() to clear the mocks.

Conclusion

We can add tests and mock any data fetching code with Jest and React’s test library.

Categories
React

React Testing — Timers, and Snapshots

Automated tests are important for most apps.

In this article, we’ll take a look at how to write tests for React components.

Timers

We can mock timers in our React component tests.

For example, if we have:

import React, { useEffect } from "react";

export default function Card({ onSelect }) {
  useEffect(() => {
    const timeoutID = setTimeout(() => {
      onSelect(null);
    }, 5000);
    return () => {
      clearTimeout(timeoutID);
    };
  }, [onSelect]);

  return (
    <>
      <button
        data-testid='button'
        onClick={() => onSelect(1)}
      >
        button
    </button>
    </>
  );
}

Then we can test it by writing the following:

Card.test.js

import React from "react";
import { render, unmountComponentAtNode } from "react-dom";
import { act } from "react-dom/test-utils";

import Card from "./card";

jest.useFakeTimers();

let container = null;
beforeEach(() => {
  container = document.createElement("div");
  document.body.appendChild(container);
});

afterEach(() => {
  unmountComponentAtNode(container);
  container.remove();
  container = null;
});

it("should select null after timing out", () => {
  const onSelect = jest.fn();
  act(() => {
    render(<Card onSelect={onSelect} />, container);
  });

  act(() => {
    jest.advanceTimersByTime(100);
  });
  expect(onSelect).not.toHaveBeenCalled();

  act(() => {
    jest.advanceTimersByTime(5000);
  });
  expect(onSelect).toHaveBeenCalledWith(null);
});

it("should clean up on being removed", () => {
  const onSelect = jest.fn();
  act(() => {
    render(<Card onSelect={onSelect} />, container);
  });
  act(() => {
    render(null, container);
  });
  act(() => {
    jest.advanceTimersByTime(5000);
  });
  expect(onSelect).not.toHaveBeenCalled();
});

it("should accept selections", () => {
  const onSelect = jest.fn();
  act(() => {
    render(<Card onSelect={onSelect} />, container);
  });
  act(() => {
    container
      .querySelector("[data-testid='button']")
      .dispatchEvent(new MouseEvent("click", { bubbles: true }));
  });
  expect(onSelect).toHaveBeenCalledWith(1);
});

We have the usual beforeEach and afterEach hooks to create and remove the container for mounting our component for testing.

In the ‘should select null after timing out’, we set the timer to the time we want by calling the jest.advanceTimerByTime method.

Then we check what the mockedonSelect function is called with toHaveBeenCalledWith .

And we check if onSelect is called with toHaveBeenCalled .

In the “should clean up on being removed” test, we make sure that onSelect isn’t called after it’s unmounted.

In the “should accept selections” test, we triggered the click event on the button.

Then we check is onSelect is called with the argument we expect.

Snapshot Testing

We can test snapshots, which is the rendered component at a given time.

For example, if we have:

import React from "react";

export default function Hello({ name }) {
  return (
    <>
      <p>hello {name}</p>
    </>
  );
}

Then we can add the following test to test the component:

Hello.test.js

import React from "react";
import { render, unmountComponentAtNode } from "react-dom";
import { act } from "react-dom/test-utils";
import pretty from "pretty";
import Hello from "./hello";

let container = null;
beforeEach(() => {
  container = document.createElement("div");
  document.body.appendChild(container);
});

afterEach(() => {
  unmountComponentAtNode(container);
  container.remove();
  container = null;
});

it("should render a greeting", () => {
  act(() => {
    render(<Hello />, container);
  });
  expect(pretty(container.innerHTML)).toMatchInlineSnapshot(`"<p>hello </p>"`);

  act(() => {
    render(<Hello name="james" />, container);
  });
  expect(pretty(container.innerHTML)).toMatchInlineSnapshot(
    `"<p>hello james</p>"`
  );
});

We have the same beforeEach and afterEach hooks as before.

To test the component, we get the rendered HTML with container.innerHTML .

Then we check it against the HTML code with toMatchInlineSnapshot .

We have to install pretty and prettier by running:

npm i pretty prettier --save-dev

to get the pretty function to render the HTML.

Conclusion

We can mock timers and test React components with rendered HTML.

Categories
React

React Testing — Mocking Modules and Dispatching Events

Automated tests are important for most apps.

In this article, we’ll take a look at how to write tests for React components.

Mocking Modules

We can mock modules that don’t work well in a test environment.

For example, if we have the following components:

Map.js

import React from "react";

import { LoadScript, GoogleMap } from "react-google-maps";
export default function Map(props) {
  return (
    <LoadScript id="script-loader" googleMapsApiKey="YOUR_API_KEY">
      <GoogleMap id="example-map" center={props.center} />
    </LoadScript>
  );
}

Contact.js

import Map from "./map";

export default function Contact({ name, email }) {
  return (
    <div>
      <address>
        {name} {email}
      </address>
      <Map center={props.center} />
    </div>
  );
}

Then we can test the Contact component with a mocked Map component by writing:

Contact.test.js

import React from "react";
import { render, unmountComponentAtNode } from "react-dom";
import { act } from "react-dom/test-utils";

import Contact from "./contact";

jest.mock("./map", () => {
  return function DummyMap(props) {
    return (
      <div data-testid="map">
        {props.center.lat}:{props.center.long}
      </div>
    );
  };
});

let container = null;
beforeEach(() => {
  container = document.createElement("div");
  document.body.appendChild(container);
});

afterEach(() => {
  unmountComponentAtNode(container);
  container.remove();
  container = null;
});

it("should render contact information", () => {
  const center = { lat: 0, long: 0 };
  act(() => {
    render(
      <Contact
        name="james"
        email="test@example.com"
        center={center}
      />,
      container
    );
  });

  expect(
    container.querySelector("address").textContent
  )
    .toContain("james test@example.com");

  expect(container.querySelector('[data-testid="map"]').textContent).toEqual(
    "0:0"
  );
});

We have:

jest.mock("./map", () => {
  return function DummyMap(props) {
    return (
      <div data-testid="map">
        {props.center.lat}:{props.center.long}
      </div>
    );
  };
});

to mock the Map component.

Then when we call render , we render with DummyMap instead of the actual Map component.

Events

To test events, we can dispatch real DOM events on DOM elements.

For instance, if we want to test the Toggle component:

import React, { useState } from "react";

export default function Toggle(props) {
  const [state, setState] = useState(false);
  return (
    <button
      onClick={() => {
        setState(previousState => !previousState);
        props.onChange(!state);
      }}
      data-testid="toggle"
    >
      {state ? "off" : "on"}
    </button>
  );
}

Then we can add a test file for it by writing:

Toggle.test.js

import React from "react";
import { render, unmountComponentAtNode } from "react-dom";
import { act } from "react-dom/test-utils";

import Toggle from "./toggle";

let container = null;
beforeEach(() => {
  container = document.createElement("div");
  document.body.appendChild(container);
});

afterEach(() => {
  unmountComponentAtNode(container);
  container.remove();
  container = null;
});

it("changes value when clicked", () => {
  const onChange = jest.fn();
  act(() => {
    render(<Toggle onChange={onChange} />, container);
  });
  const button = document.querySelector("[data-testid=toggle]");
  expect(button.innerHTML).toBe("on");
  act(() => {
    button.dispatchEvent(new MouseEvent("click", { bubbles: true }));
  });
  expect(onChange).toHaveBeenCalledTimes(1);
  expect(button.innerHTML).toBe("off");
  act(() => {
    button.dispatchEvent(new MouseEvent("click", { bubbles: true }));
  });
  expect(onChange).toHaveBeenCalledTimes(2);
  expect(button.innerHTML).toBe("on");
});

We mock the onChange method that we pass in as the value of the onChange prop.

Then we get the button with the selector[data-testid=toggle] from the Toggle component.

Then we can get the content of the button and how many times onChange has been called after we call dispatchEvent to dispatch a click MouseEvent .

We need to pass in { bubbles: true } so that React will delegate the event to the document.

Conclusion

We can mock modules that we can’t use conveniently in our tests.

Also, we can trigger events on elements and check the result after that.