Hướng dẫn thử thách
1 / 1
Build an RPG Creature Search App
In this project, you'll build an app that will search for creatures from an RPG game by name or ID and display the results to the user. To retrieve the creature data and images, you'll use freeCodeCamp's <a href="https://rpg-creature-api.freecodecamp.rocks/" target="_blank" rel="noopener noreferrer nofollow">RPG Creature API</a>.
**Note:** The first 13 steps must be completed inside the `index.html` file.
**Objective:** Build an app that is functionally similar to <a href="https://rpg-creature-search-app.freecodecamp.rocks" target="_blank" rel="noopener noreferrer nofollow">https://rpg-creature-search-app.freecodecamp.rocks</a>.
**User Stories:**
1. You should have an `input` element with an `id` of `"search-input"`, and is required.
1. You should have a `button` element with an `id` of `"search-button"`.
1. You should have an element with an `id` of `"creature-name"`.
1. You should have an element with an `id` of `"creature-id"`.
1. You should have an element with an `id` of `"weight"`.
1. You should have an element with an `id` of `"height"`.
1. You should have an element with an `id` of `"types"`.
1. You should have an element with an `id` of `"hp"`.
1. You should have an element with an `id` of `"attack"`.
1. You should have an element with an `id` of `"defense"`.
1. You should have an element with an `id` of `"special-attack"`.
1. You should have an element with an `id` of `"special-defense"`.
1. You should have an element with an `id` of `"speed"`.
1. When the `#search-input` element contains the value `Red` and the `#search-button` element is clicked, an alert should appear with the text `"Creature not found"`.
1. When the `#search-input` element contains the value `Pyrolynx` and the `#search-button` element is clicked, the values in the `#creature-name`, `#creature-id`, `#weight`, `#height`, `#hp`, `#attack`, `#defense`, `#special-attack`, `#special-defense`, and `#speed` elements should be `PYROLYNX`, `#1` or `1`, `Weight: 42` or `42`, `Height: 32` or `32`, `65`, `80`, `50`, `90`, `55`, and `100`, respectively.
1. When the `#search-input` element contains the value `Pyrolynx` and the `#search-button` element is clicked, a single element should be added within the `#types` element that contains the text `FIRE`. The `#types` element content should be cleared between searches.
1. When the `#search-input` element contains the value `2` and the `#search-button` element is clicked, the values in the `#creature-name`, `#creature-id`, `#weight`, `#height`, `#hp`, `#attack`, `#defense`, `#special-attack`, `#special-defense`, and `#speed` elements should be `AQUOROC`, `#2` or `2`, `Weight: 220` or `220`, `Height: 53` or `53`, `85`, `90`, `120`, `60`, `70`, and `40`, respectively.
1. When the `#search-input` element contains the value `2` and the `#search-button` element is clicked, two elements should be added within the `#types` element that contain text values `WATER` and `ROCK`, respectively. The `#types` element content should be cleared between searches.
1. When the `#search-input` element contains an invalid creature name, and the `#search-button` element is clicked, an alert should appear with the text `"Creature not found"`.
1. When the `#search-input` element contains a valid creature ID and the `#search-button` element is clicked, the UI should be filled with the correct data.
Fulfill the user stories and pass all the tests below to complete this project. Give it your own personal style. Happy Coding!
**Note:** When running the tests there will be a slight delay. Please wait a few seconds to allow the tests to finish. Do not refresh the page before they are done.
# --before-all--
```js
const creatures = [
{ id: 1, name: "Pyrolynx", weight: 42, height: 32, special: { name: "Blazing Reflex", description: "Increases speed when hit by a Fire-type move." }, stats: [{ base_stat: 65, name: "hp" }, { base_stat: 80, name: "attack" }, { base_stat: 50, name: "defense" }, { base_stat: 90, name: "special-attack" }, { base_stat: 55, name: "special-defense" }, { base_stat: 100, name: "speed" }], types: [{ name: "fire" }] },
{ id: 2, name: "Aquoroc", weight: 220, height: 53, special: { name: "Tidal Barrier", description: "Reduces damage from Fire and Ice attacks by 50%." }, stats: [{ base_stat: 85, name: "hp" }, { base_stat: 90, name: "attack" }, { base_stat: 120, name: "defense" }, { base_stat: 60, name: "special-attack" }, { base_stat: 70, name: "special-defense" }, { base_stat: 40, name: "speed" }], types: [{ name: "water" }, { name: "rock" }] },
{ id: 3, name: "Voltadon", weight: 310, height: 73, special: { name: "Stormcaller", description: "Summons a thunderstorm, boosting Electric-type moves." }, stats: [{ base_stat: 92, name: "hp" }, { base_stat: 115, name: "attack" }, { base_stat: 75, name: "defense" }, { base_stat: 100, name: "special-attack" }, { base_stat: 80, name: "special-defense" }, { base_stat: 85, name: "speed" }], types: [{ name: "electric" }, { name: "dragon" }] },
{ id: 4, name: "Floraspine", weight: 38, height: 33, special: { name: "Toxic Bloom", description: "Releases a poison cloud when hit by a contact move." }, stats: [{ base_stat: 78, name: "hp" }, { base_stat: 55, name: "attack" }, { base_stat: 80, name: "defense" }, { base_stat: 85, name: "special-attack" }, { base_stat: 90, name: "special-defense" }, { base_stat: 50, name: "speed" }], types: [{ name: "grass" }, { name: "poison" }] },
{ id: 5, name: "Cryostag", weight: 160, height: 67, special: { name: "Frozen Veil", description: "Reduces damage from Fire-type moves by 30%." }, stats: [{ base_stat: 85, name: "hp" }, { base_stat: 75, name: "attack" }, { base_stat: 65, name: "defense" }, { base_stat: 110, name: "special-attack" }, { base_stat: 105, name: "special-defense" }, { base_stat: 95, name: "speed" }], types: [{ name: "ice" }, { name: "fairy" }] },
{ id: 6, name: "Terradon", weight: 390, height: 87, special: { name: "Stone Glide", description: "Immune to Rock-type attacks." }, stats: [{ base_stat: 98, name: "hp" }, { base_stat: 120, name: "attack" }, { base_stat: 85, name: "defense" }, { base_stat: 60, name: "special-attack" }, { base_stat: 80, name: "special-defense" }, { base_stat: 102, name: "speed" }], types: [{ name: "ground" }, { name: "flying" }] },
{ id: 7, name: "Emberapod", weight: 19, height: 20, special: { name: "Scorch Shell", description: "Contact attackers take burn damage." }, stats: [{ base_stat: 55, name: "hp" }, { base_stat: 70, name: "attack" }, { base_stat: 50, name: "defense" }, { base_stat: 100, name: "special-attack" }, { base_stat: 65, name: "special-defense" }, { base_stat: 120, name: "speed" }], types: [{ name: "fire" }, { name: "bug" }] },
{ id: 8, name: "Lunaclaw", weight: 92, height: 58, special: { name: "Moonlit Fury", description: "Becomes stronger at night." }, stats: [{ base_stat: 75, name: "hp" }, { base_stat: 85, name: "attack" }, { base_stat: 60, name: "defense" }, { base_stat: 95, name: "special-attack" }, { base_stat: 100, name: "special-defense" }, { base_stat: 88, name: "speed" }], types: [{ name: "dark" }, { name: "psychic" }] },
{ id: 9, name: "Quillquake", weight: 145, height: 40, special: { name: "Armor Spikes", description: "Deals damage when hit by contact moves." }, stats: [{ base_stat: 80, name: "hp" }, { base_stat: 105, name: "attack" }, { base_stat: 120, name: "defense" }, { base_stat: 50, name: "special-attack" }, { base_stat: 75, name: "special-defense" }, { base_stat: 40, name: "speed" }], types: [{ name: "ground" }, { name: "steel" }] },
{ id: 10, name: "Mystifin", weight: 88, height: 60, special: { name: "Spectral Current", description: "Gains evasion when in rain." }, stats: [{ base_stat: 90, name: "hp" }, { base_stat: 60, name: "attack" }, { base_stat: 75, name: "defense" }, { base_stat: 115, name: "special-attack" }, { base_stat: 100, name: "special-defense" }, { base_stat: 80, name: "speed" }], types: [{ name: "water" }, { name: "ghost" }] },
{ id: 11, name: "Dracilume", weight: 280, height: 77, special: { name: "Inferno Heart", description: "Boosts Fire-type moves when HP is below 50%." }, stats: [{ base_stat: 95, name: "hp" }, { base_stat: 105, name: "attack" }, { base_stat: 85, name: "defense" }, { base_stat: 120, name: "special-attack" }, { base_stat: 80, name: "special-defense" }, { base_stat: 90, name: "speed" }], types: [{ name: "fire" }, { name: "dragon" }] },
{ id: 12, name: "Thornaconda", weight: 315, height: 187, special: { name: "Vine Trap", description: "Lowers the opponent's Speed when hit by a physical move." }, stats: [{ base_stat: 100, name: "hp" }, { base_stat: 115, name: "attack" }, { base_stat: 90, name: "defense" }, { base_stat: 50, name: "special-attack" }, { base_stat: 80, name: "special-defense" }, { base_stat: 60, name: "speed" }], types: [{ name: "grass" }, { name: "dark" }] },
{ id: 13, name: "Frostbyte", weight: 88, height: 50, special: { name: "Static Freeze", description: "Has a 30% chance to paralyze and freeze the opponent." }, stats: [{ base_stat: 75, name: "hp" }, { base_stat: 90, name: "attack" }, { base_stat: 70, name: "defense" }, { base_stat: 105, name: "special-attack" }, { base_stat: 85, name: "special-defense" }, { base_stat: 110, name: "speed" }], types: [{ name: "ice" }, { name: "electric" }] },
{ id: 14, name: "Graviboa", weight: 560, height: 150, special: { name: "Gravity Coil", description: "Halves the opponent's Speed when it enters battle." }, stats: [{ base_stat: 110, name: "hp" }, { base_stat: 85, name: "attack" }, { base_stat: 130, name: "defense" }, { base_stat: 65, name: "special-attack" }, { base_stat: 100, name: "special-defense" }, { base_stat: 45, name: "speed" }], types: [{ name: "rock" }, { name: "psychic" }] },
{ id: 15, name: "Zephyreon", weight: 60, height: 37, special: { name: "Tailwind Grace", description: "Doubles Speed for the first 3 turns of battle." }, stats: [{ base_stat: 70, name: "hp" }, { base_stat: 65, name: "attack" }, { base_stat: 60, name: "defense" }, { base_stat: 110, name: "special-attack" }, { base_stat: 95, name: "special-defense" }, { base_stat: 125, name: "speed" }], types: [{ name: "flying" }, { name: "fairy" }] },
{ id: 16, name: "Blazebore", weight: 250, height: 54, special: { name: "Magma Charge", description: "Has a 20% chance to set the battlefield on fire, dealing chip damage to non-Fire types." }, stats: [{ base_stat: 90, name: "hp" }, { base_stat: 120, name: "attack" }, { base_stat: 80, name: "defense" }, { base_stat: 75, name: "special-attack" }, { base_stat: 60, name: "special-defense" }, { base_stat: 85, name: "speed" }], types: [{ name: "fire" }, { name: "ground" }] },
{ id: 17, name: "Brontogale", weight: 700, height: 110, special: { name: "Aeroquake", description: "Summons strong winds that increase the power of Flying-type moves." }, stats: [{ base_stat: 115, name: "hp" }, { base_stat: 95, name: "attack" }, { base_stat: 105, name: "defense" }, { base_stat: 75, name: "special-attack" }, { base_stat: 90, name: "special-defense" }, { base_stat: 55, name: "speed" }], types: [{ name: "flying" }, { name: "rock" }] },
{ id: 18, name: "Shadeelisk", weight: 112, height: 69, special: { name: "Toxic Mirage", description: "Creates an illusion that confuses the opponent for the first two turns." }, stats: [{ base_stat: 80, name: "hp" }, { base_stat: 85, name: "attack" }, { base_stat: 70, name: "defense" }, { base_stat: 110, name: "special-attack" }, { base_stat: 100, name: "special-defense" }, { base_stat: 95, name: "speed" }], types: [{ name: "dark" }, { name: "poison" }] },
{ id: 19, name: "Titanule", weight: 490, height: 77, special: { name: "Iron Tide", description: "Raises Defense in rainy weather." }, stats: [{ base_stat: 130, name: "hp" }, { base_stat: 95, name: "attack" }, { base_stat: 140, name: "defense" }, { base_stat: 60, name: "special-attack" }, { base_stat: 90, name: "special-defense" }, { base_stat: 30, name: "speed" }], types: [{ name: "steel" }, { name: "water" }] },
{ id: 20, name: "Faegis", weight: 55, height: 30, special: { name: "Enchanted Shield", description: "Blocks the first super-effective attack in battle." }, stats: [{ base_stat: 85, name: "hp" }, { base_stat: 50, name: "attack" }, { base_stat: 120, name: "defense" }, { base_stat: 100, name: "special-attack" }, { base_stat: 115, name: "special-defense" }, { base_stat: 55, name: "speed" }], types: [{ name: "fairy" }, { name: "steel" }] },
];
// a real response arrives in a later task, never in a microtask, so the stub
// resolves from a timer to keep that ordering
const _respondWith = (body) =>
new Promise((resolve) =>
setTimeout(
() => {
const found = body !== undefined;
const responseText = found
? JSON.stringify(body)
: "Invalid creature name or ID";
resolve({
ok: found,
status: found ? 200 : 404,
statusText: found ? "OK" : "Not Found",
json: () => Promise.resolve().then(() => JSON.parse(responseText)),
text: () => Promise.resolve(responseText),
});
},
0
)
);
window.fetch = (url) => {
const path = String(url).split("?")[0].replace(/\/+$/, "");
const lastSegment = decodeURIComponent(path.split("/").pop());
// GET /api/creatures -> list of every creature's id and name
if (lastSegment.toLowerCase() === "creatures") {
return _respondWith(creatures.map(({ id, name }) => ({ id, name })));
}
// GET /api/creature/<name-or-id> -> a single creature
const key = lastSegment.toLowerCase();
const creature = creatures.find(
(c) => c.name.toLowerCase() === key || String(c.id) === key
);
return _respondWith(creature);
};
```
Vượt qua bài kiểm tra hiện tại để mở khóa bài tiếp theo.
main.sh
UTF-8 • Tab Size: 2Kiểm tra bài:⌘↵