React Testing Library: Klawiatura

Opublikowano: 14.12.2023 - tagi: JavaScript React Testowanie Test Klawiatura

Testowanie klawiatury

Do symulacji obsługi klawiatury posłuży nam niezawodna biblioteka user-event.

Załóżmy, że mam zwykłe pole tekstowe. Po wpisaniu danych i wciśnięciu przycisku Enter wartość powinna zostać dodana do listy.

Przykład:

import { useState } from "react";

export function MyComponent() {
    const [tasks, setTasks] = useState([]);
    const [task, setTask] = useState('');

    const onChangeTask = (e) => {
        setTask(e.target.value)
    };

    const onAddTask = (e) => {
        if (e.code == 'Enter' && task) {
            setTasks([task, ...tasks]);
            setTask('');
        }
    }

    return (
        <>
            <div>
                <label htmlFor="newTask">Task title:</label>
                <input
                    type="text"
                    id="newTask"
                    value={task}
                    onChange={onChangeTask}
                    onKeyDown={onAddTask}
                />
            </div>
            <ul>
                {
                    tasks.map((task, index) => (
                        <li key={index}>
                            {task}
                        </li>
                    ))
                }
            </ul>
        </>
    )
}

Test:

import {render, screen, within} from '@testing-library/react';
const { getByLabelText, getByTestId } = screen;

const renderComponent = () => {
    render(<MyComponent />);

    const addTaskField = getByLabelText('Task title:');
    const taskList = within(getByTestId('task-list'));
    const addTask = async (newTask) => {
        await userEvent.clear(addTaskField)
        await userEvent.type(addTaskField, newTask);
        await userEvent.keyboard('[Enter]');
    }

    return {
        addTaskField,
        taskList,
        addTask
    }
}

test('should add new task to the list', async () => {
    // given
    const { taskList, addTask } = renderComponent();

    // when
    await addTask('My task');

    // then
    expect(taskList.getByText('My task'));
});

Za dodanie zadania do listy odpowiedzialna jest w tym teście funkcja addTask. Wywołana jest tam funkcja keyboard z biblioteki user event, która w tym przypadku symuluje wciśnięcie przycisku Enter na klawiaturze.


Podsumowanie: Listopad 2023

Opublikowano: 30.11.2023 - tagi: Blog Podsumowanie Listopad 2023

We listopadzie opublikowałem 6 wpisów:


Swfit

  1. Klasy
  2. Protokoły
  3. Kontrola dostępu
  4. Extension

Narysowałem dwa komiksy:

  1. Coding rules
  2. Face the problem

Przeczytałem dwie książki:

  1. Outpost 2 — Dmitry Glukhovsky
  2. Dawno temu w Warszawie — Jakub Żulczyk

Przesłuchałem pięć audiobooki:

  1. Rozjemca — Brandon Sanderson
  2. Powróceni — Abdulrazak Gurnah
  3. Zło ze wschodu — Andrzej Pilipiuk
  4. Legion — Elżbieta Cherezińska
  5. Ziemiomorze. Czarnoksiężnik z Archipelagu — Ursula K. Le Guin

Comics: Face the problem

Opublikowano: 23.11.2023 - tagi: Komiks Rysowanie

Face the problem

Swift: Extension

Opublikowano: 21.11.2023 - tagi: Swift Struktura Klasa Protokół Typ Rozszerzenie

Rozszerzenia

Rozszerzenia w Swift służą do dodania funkcjonalności do już istniejącego typu. Może to być typ zarówno wbudowany w język, jak i stworzony przez programistę.

Rozszerzenie można dodać do klasy, struktury, protokołu, enumeracji i innych typów w Swift.

Żeby dodać nowe rozszerzenie, należy użyć słowa kluczowego extension.

Za pomocą rozszerzenie możesz dodać nową funkcjonalność, ale nie możesz nadpisać już istniejącej.

Przykłady

Typ własny

class Calculator {
	func add(a: Int, b: Int) -> Int {
		return a + b
	}
}

extension Calculator {
	func subtract(a: Int, b: Int) -> Int {
		return a - b
	}
}

var calculator = Calculator()

print(calculator.add(a: 7, b: 3)) // 10
print(calculator.subtract(a: 7, b: 3)) // 4

Typ wbudowany

Możesz też dodać rozszerzenie do typu wbudowanego:

extension Int {
	func square() -> Int {
	    return self * self
	}
}

var a: Int = 7
print("a * a =  \(a.square())") // 49
print("a = \(a)") // 7

Jeśli chcesz zmienić stan użyj słowa kluczowego mutating i nową wartość przypisz do self:

extension Int {
	mutating func square() {
	    self = self * self
	}
}

var a: Int = 7
a.square()
print("a = \(a)") // 49
a.square()
print("a = \(a)") // 2401

Computed property

Za pomocą rozszerzenia nie możesz dodać stored property:

extension SomeType {
	var property1: Int // Błąd!
}

ale możesz dodać computed property:

struct User {
	var firstName: String = ""
	var lastName: String = ""
}

extension User {
	var fullName: String {
		return firstName + " " + lastName
	}
}

var user = User(firstName: "James", lastName: "Bond");
print(user.fullName) // James Bond

Swift: Kontrola dostępu

Opublikowano: 16.11.2023 - tagi: Swift Klasa Struktura Dostęp

Kontrola dostępu w Swift

Dzięki kontroli dostępu możesz określić dostępność klas, struktur, enumeracji, protokołach, właściwości, metod itp.

Swift udostępnia kilka poziomów dostępu.

Są to:

  1. open
  2. public
  3. private
  4. fileprivate
  5. internal

Dostęp: open

Poziom dostępu open pozwala na to, że każdy ma do tego dostęp: zarówno z poziomu modułu jak i z zewnątrz.

Dzięki temu możesz pozwolić sobie także na nadpisywanie.

Jest to najmniej restrykcyjnym poziomem dostępu w Swift.

Dostęp: public

Jeśli określisz coś jako public to znaczy, że informujesz, że każdy może mieć do tego dostęp.

Podobne do open, ale różnica między nimi jest taka, że masz do tego dostęp wszędzie, ale nie możesz sobie pozwolić na nadpisanie z poziomu innego modułu.

Na przykład:

class MyClass {
	public name: String = ""
}

var obj = MyClass()

obj.name = "Public"

print(obj.name) // Public

Dostęp: private

private sprawia, że dostęp jest ograniczony do danego zakresu.

class User {
	private firstName: String = ""
	private lastName: String = ""
	
	init(firstName: String, lastName: String) {
		self.firstName = firstName
		self.lastName = lastName
	}
	
	func getFullName() -> String {
		return "\(firstName) \(lastName)"
	}
}

var user = User(firstName: "Jan", lastName: "Kowalski")

print(user.getFullName())

user.firstName = "Adam" // Błąd!

Dostęp: fileprivate

Ten poziom dostępu podobny jest do private. Różnica jest taka, że dostęp jest ograniczony w obrębie pliku.

class User {
	fileprivate firstName: String = ""
	fileprivate lastName: String = ""
	
	init(firstName: String, lastName: String) {
		self.firstName = firstName
		self.lastName = lastName
	}
	
	func getFullName() -> String {
		return "\(firstName) \(lastName)"
	}
}

var user = User(firstName: "Jan", lastName: "Kowalski")

print(user.getFullName()) // Jan Kowalski

user.firstName = "Adam" 

print(user.getFullName()) // Adam Kowalski

Jeśli spróbujesz się dostać do firstName lub lastName w innym pliku, zostanie zgłoszony błąd.

Inny przykład:

fileprivate class MyClass {
	name: String = ""
}

Tak oznaczoną klasę: MyClass możesz używać tylko w pliku, w którym jest stworzona.

Dostęp: internal

internal to domyślny poziom dostępu w Swift.

Czyli zapis:

class MyClass {
	...
}

jest równoznaczny z:

internal class MyClass {
	...
}

A co to robi? W taki sposób informujemy, że kod jest dostępny w obrębie modułu, ale na zewnątrz już nie.