Build a Pokémon Search App Project

Hey, for whatever reason I can’t fulfill steps 19 and 20 which are:
When the #search-input element contains an invalid Pokemon name and the #search-button element is clicked, an alert should appear with the text "Pokémon not found" .
When the #search-input element contains a valid Pokemon id and the #search-button element is clicked, the UI should be filled with the correct data.

I also get these errors in the console when I run the tests:
[Error: TypeError: Cannot read properties of undefined (reading ‘trim’)]
[Error: AssertionError: expected to have a length of 2 but got +0]

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Pokémon Search App</title>
    <link rel="stylesheet" href="styles.css">
  </head>
  <body>
    <h1>Pokémon Search App</h1>
    <input type="text" id="search-input" placeholder="Enter Pokémon name or ID" required>
    <button id="search-button">Search</button>
    <div id="pokemon-info">
      <p id="pokemon-name" class="pokemon-stat"></p>
      <p id="pokemon-id" class="pokemon-stat"></p>
      <p id="weight" class="pokemon-stat"></p>
      <p id="height" class="pokemon-stat"></p>
      <p id="types" class="pokemon-stat"></p>
      <p id="hp" class="pokemon-stat"></p>
      <p id="attack" class="pokemon-stat"></p>
      <p id="defense" class="pokemon-stat"></p>
      <p id="special-attack" class="pokemon-stat"></p>
      <p id="special-defense" class="pokemon-stat"></p>
      <p id="speed" class="pokemon-stat"></p>
    </div>
    <script src="script.js"></script>
  </body>
</html>

script.js

document.getElementById("search-button").addEventListener("click", () => {
  const query = document.getElementById("search-input").value.trim().toLowerCase();

  if (!query) {
    return;
  }

  const url = `https://pokeapi.co/api/v2/pokemon/${query}`;

  fetch(url)
    .then(response => {
      if (!response.ok) {
        throw new Error("Pokémon not found");
      }
      return response.json();
    })
    .then(data => {
      updatePokemonInfo(data);
    })
    .catch(error => {
      if (error.message === "Pokémon not found") {
        alert(error.message);
      } else {
        alert("Pokémon not found");
      }
      clearPokemonInfo();
    });
});

function updatePokemonInfo(data) {
  document.getElementById("pokemon-name").innerText = data.name.toUpperCase();
  document.getElementById("pokemon-id").innerText = `#${data.id}`;
  document.getElementById("weight").innerText = `Weight: ${data.weight}`;
  document.getElementById("height").innerText = `Height: ${data.height}`;
  document.getElementById("hp").innerText = data.stats[0].base_stat;
  document.getElementById("attack").innerText = data.stats[1].base_stat;
  document.getElementById("defense").innerText = data.stats[2].base_stat;
  document.getElementById("special-attack").innerText = data.stats[3].base_stat;
  document.getElementById("special-defense").innerText = data.stats[4].base_stat;
  document.getElementById("speed").innerText = data.stats[5].base_stat;

  const typesContainer = document.getElementById("types");
  typesContainer.innerHTML = "";
  data.types.forEach(typeInfo => {
    const typeElement = document.createElement("p");
    typeElement.innerText = typeInfo.type.name.toUpperCase();
    typesContainer.appendChild(typeElement);
  });

  let sprite = document.getElementById("sprite");
  if (!sprite) {
    sprite = document.createElement("img");
    sprite.id = "sprite";
    document.getElementById("pokemon-info").appendChild(sprite);
  }
  sprite.src = data.sprites.front_default;

  document.getElementById("pokemon-info").style.display = "flex";
}

function clearPokemonInfo() {
  document.getElementById("pokemon-name").innerText = "";
  document.getElementById("pokemon-id").innerText = "";
  document.getElementById("weight").innerText = "";
  document.getElementById("height").innerText = "";
  document.getElementById("hp").innerText = "";
  document.getElementById("attack").innerText = "";
  document.getElementById("defense").innerText = "";
  document.getElementById("special-attack").innerText = "";
  document.getElementById("special-defense").innerText = "";
  document.getElementById("speed").innerText = "";
  document.getElementById("types").innerHTML = "";

  const sprite = document.getElementById("sprite");
  if (sprite) {
    sprite.remove();
  }

  document.getElementById("pokemon-info").style.display = "none";
}

That url for api doesn’t seem to be https://pokeapi-proxy.freecodecamp.rocks

It should work with the real API.

The new tests for hard-coded solutions are the problem. I can get the last test to pass if I remove your DOM clearing code.

But for the test for an invalid name, I have to increase the timeout delay in the test. Which is a little odd as it is basically the same as the test for “Red” as the invalid name. Not sure why the API response for “Red” is faster than the response for a random string, but it somehow is.


Without the DOM clearing code, your code should technically pass.

Edit: actually, both tests passes with your current code as long as I increase the timeout delay in the test. I might make a PR for it. Edit: I made a PR for it.

This makes me wonder though whether it should be expected for tests to pass with real API. It doesn’t mean functionality wise it won’t work with real API.

Considering our API is a proxy API, I would say yes.

Sure, we might not keep the proxy fully future compliant, so if the real API has breaking changes added to the V2 API, we might not update the proxy to reflect that. But that is unlikely, it is a versioned API for a reason, so breaking changes should be added to new versions of the API.

Edit: just to be clear, the proxy pulls from the real API

https://github.com/freeCodeCamp/demo-projects/blob/main/apps/pokeapi-proxy/api/pokemon/pokemon.handlers.mjs

oh yeah my problem was I didn’t read the start of the instructions clearly. They wanted me to use their own API instead of any API. tbh I googled “Pokemon API” and clicked the first one so even though it ran as intended the issue was because I wasn’t using freecodecamp’s API.

I see it provided more as a service, not a requirement.

It is using the same API behind the scene, creating a proxy for it to avoid various issues. It has some caching as well, and so we don’t send thousands of extra API requests their way by asking campers to use their API.

There are already a bunch of wrappers for it.

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.