diff --git a/src/components/Checkbox/Checkbox.test.tsx b/src/components/Checkbox/Checkbox.test.tsx index 1d5e919e..55e537b6 100644 --- a/src/components/Checkbox/Checkbox.test.tsx +++ b/src/components/Checkbox/Checkbox.test.tsx @@ -64,13 +64,19 @@ describe("렌더링", () => { expect(checkbox).toBeChecked(); }); - test("invalid가 true이면 aria-invalid가 설정된다.", () => { + test("invalid prop이 없을 때 aria-invalid 속성이 올바르게 설정된다", () => { + render(); + const checkbox = screen.getByRole("checkbox"); + expect(checkbox).toHaveAttribute("aria-invalid", "false"); + }); + + test("invalid가 true일 때 aria-invalid 속성이 올바르게 설정된다", () => { render(); const checkbox = screen.getByRole("checkbox"); expect(checkbox).toHaveAttribute("aria-invalid", "true"); }); - test("invalid가 false이면 aria-invalid가 false로 설정된다.", () => { + test("invalid가 false일 때 aria-invalid 속성이 올바르게 설정된다", () => { render(); const checkbox = screen.getByRole("checkbox"); expect(checkbox).toHaveAttribute("aria-invalid", "false"); @@ -142,13 +148,19 @@ describe("렌더링", () => { ).not.toBeInTheDocument(); }); - test("required가 true이면 aria-required가 true로 설정된다.", () => { + test("required prop이 없을 때 aria-required 속성이 올바르게 설정된다", () => { + render(); + const checkbox = screen.getByRole("checkbox"); + expect(checkbox).not.toHaveAttribute("aria-required"); + }); + + test("required가 true일 때 aria-required 속성이 올바르게 설정된다", () => { render(); const checkbox = screen.getByRole("checkbox"); expect(checkbox).toHaveAttribute("aria-required", "true"); }); - test("required가 false이면 aria-required가 설정되지 않는다.", () => { + test("required가 false일 때 aria-required 속성이 올바르게 설정된다", () => { render(); const checkbox = screen.getByRole("checkbox"); expect(checkbox).not.toHaveAttribute("aria-required"); diff --git a/src/components/PasswordInput/PasswordInput.test.tsx b/src/components/PasswordInput/PasswordInput.test.tsx index 731c6f26..0c5bdf53 100644 --- a/src/components/PasswordInput/PasswordInput.test.tsx +++ b/src/components/PasswordInput/PasswordInput.test.tsx @@ -82,6 +82,42 @@ test("접근성 속성을 제공한다 (aria-label, aria-pressed)", async () => expect(toggle).toHaveAttribute("aria-pressed", "true"); }); +test("invalid prop이 없을 때 aria-invalid 속성이 올바르게 설정된다", () => { + render(); + const input = screen.getByLabelText(/패스워드/, { selector: "input" }); + expect(input).not.toHaveAttribute("aria-invalid"); +}); + +test("invalid가 true일 때 aria-invalid 속성이 올바르게 설정된다", () => { + render(); + const input = screen.getByLabelText(/패스워드/, { selector: "input" }); + expect(input).toHaveAttribute("aria-invalid", "true"); +}); + +test("invalid가 false일 때 aria-invalid 속성이 올바르게 설정된다", () => { + render(); + const input = screen.getByLabelText(/패스워드/, { selector: "input" }); + expect(input).not.toHaveAttribute("aria-invalid"); +}); + +test("required prop이 없을 때 aria-required 속성이 올바르게 설정된다", () => { + render(); + const input = screen.getByLabelText(/패스워드/, { selector: "input" }); + expect(input).not.toHaveAttribute("aria-required"); +}); + +test("required가 true일 때 aria-required 속성이 올바르게 설정된다", () => { + render(); + const input = screen.getByLabelText(/패스워드/, { selector: "input" }); + expect(input).toHaveAttribute("aria-required", "true"); +}); + +test("required가 false일 때 aria-required 속성이 올바르게 설정된다", () => { + render(); + const input = screen.getByLabelText(/패스워드/, { selector: "input" }); + expect(input).not.toHaveAttribute("aria-required"); +}); + test("키보드(Space/Enter)로 가시성을 토글할 수 있다", async () => { const user = userEvent.setup(); render(); diff --git a/src/components/PasswordInput/PasswordInput.tsx b/src/components/PasswordInput/PasswordInput.tsx index ab178574..8a48bf7c 100644 --- a/src/components/PasswordInput/PasswordInput.tsx +++ b/src/components/PasswordInput/PasswordInput.tsx @@ -23,6 +23,7 @@ export interface PasswordInputProps extends Omit< */ export function PasswordInput({ invalid = false, + required = false, disabled = false, placeholder = "패스워드를 입력해주세요.", ref, @@ -50,6 +51,7 @@ export function PasswordInput({ className={inputStyles()} aria-label="패스워드" aria-invalid={invalid ? true : undefined} + aria-required={required ? true : undefined} {...rest} /> { + render: (args) => { return ( - 사과 - 바나나 - 오렌지 - + /> - 사과 - 바나나 - 오렌지 - + /> ); }, + argTypes: { + name: { + control: false, + }, + label: { + control: false, + }, + orientation: { + control: false, + }, + defaultValue: { + control: false, + }, + }, }; export const GroupDisabled: Story = { @@ -115,79 +123,69 @@ export const ItemDisabled: Story = { }; export const Tones: Story = { - render: () => { + render: (args) => { return ( - 사과 - 바나나 - 오렌지 - + /> - 사과 - 바나나 - 오렌지 - + /> - 사과 - 바나나 - 오렌지 - + /> - 사과 - 바나나 - 오렌지 - + /> - 사과 - 바나나 - 오렌지 - + /> - 사과 - 바나나 - 오렌지 - + /> ); }, + argTypes: { + name: { + control: false, + }, + label: { + control: false, + }, + tone: { + control: false, + }, + }, + args: { + defaultValue: "apple", + }, }; export const Controlled = () => { diff --git a/src/components/Select/Select.test.tsx b/src/components/Select/Select.test.tsx index 2b3229df..fd002a68 100644 --- a/src/components/Select/Select.test.tsx +++ b/src/components/Select/Select.test.tsx @@ -434,7 +434,16 @@ describe("상태 관리", () => { }); describe("접근성 및 기타", () => { - test("invalid가 true일 때 오류 메시지가 표시된다", () => { + test("invalid prop이 없을 때 aria-invalid 속성이 올바르게 설정된다", () => { + render( + + React + , + ); + expect(screen.getByRole("combobox")).not.toHaveAttribute("aria-invalid"); + }); + + test("invalid가 true일 때 aria-invalid 속성이 올바르게 설정된다", () => { render( React @@ -446,7 +455,7 @@ describe("접근성 및 기타", () => { ); }); - test("invalid가 false일 때 오류 메시지가 표시되지 않는다", () => { + test("invalid가 false일 때 aria-invalid 속성이 올바르게 설정된다", () => { render( React @@ -455,6 +464,15 @@ describe("접근성 및 기타", () => { expect(screen.getByRole("combobox")).not.toHaveAttribute("aria-invalid"); }); + test("required prop이 없을 때 aria-required 속성이 올바르게 설정된다", () => { + render( + + React + , + ); + expect(screen.getByRole("combobox")).not.toHaveAttribute("aria-required"); + }); + test("required가 true일 때 aria-required 속성이 올바르게 설정된다", () => { render( diff --git a/src/components/TextInput/TextInput.test.tsx b/src/components/TextInput/TextInput.test.tsx index 49ea9ee8..628d4530 100644 --- a/src/components/TextInput/TextInput.test.tsx +++ b/src/components/TextInput/TextInput.test.tsx @@ -1,11 +1,11 @@ import { createRef } from "react"; import { render, screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; -import { describe, expect, it, vi } from "vitest"; +import { describe, expect, test, vi } from "vitest"; import { TextInput } from "./TextInput"; describe("TextInput", () => { - it("기본 props로 올바르게 렌더링되어야 합니다.", () => { + test("기본 props로 올바르게 렌더링되어야 한다.", () => { render( , ); @@ -15,7 +15,7 @@ describe("TextInput", () => { expect(inputElement).toBeInstanceOf(HTMLInputElement); }); - it("사용자가 텍스트를 입력하면 value가 변경되고 onChange 핸들러가 호출되어야 합니다.", async () => { + test("사용자가 텍스트를 입력하면 value가 변경되고 onChange 핸들러가 호출되어야 한다.", async () => { const user = userEvent.setup(); const handleChange = vi.fn(); render( @@ -35,7 +35,7 @@ describe("TextInput", () => { expect(inputElement.value).toBe("안녕하세요"); }); - it("disabled prop이 true일 때 입력이 비활성화되어야 합니다.", async () => { + test("disabled prop이 true일 때 입력이 비활성화되어야 한다.", async () => { const user = userEvent.setup(); const handleChange = vi.fn(); render( @@ -55,29 +55,50 @@ describe("TextInput", () => { expect(handleChange).not.toHaveBeenCalled(); }); - it('invalid prop이 true일 때 aria-invalid 속성이 "true"여야 합니다.', () => { - render(); - const inputElement = screen.getByTestId("text-input"); + test("invalid prop이 없을 때 aria-invalid 속성이 올바르게 설정된다", () => { + render(); + const inputElement = screen.getByRole("textbox"); + expect(inputElement).not.toHaveAttribute("aria-invalid"); + }); + + test("invalid가 true일 때 aria-invalid 속성이 올바르게 설정된다", () => { + render(); + const inputElement = screen.getByRole("textbox"); expect(inputElement).toHaveAttribute("aria-invalid", "true"); }); - it("invalid prop이 없거나 false일 때 aria-invalid 속성을 갖지 않아야 합니다.", () => { - const { rerender } = render(); - const inputElement = screen.getByTestId("text-input"); + test("invalid가 false일 때 aria-invalid 속성이 올바르게 설정된다", () => { + render(); + const inputElement = screen.getByRole("textbox"); expect(inputElement).not.toHaveAttribute("aria-invalid"); + }); - rerender(); - expect(inputElement).not.toHaveAttribute("aria-invalid"); + test("required prop이 없을 때 aria-required 속성이 올바르게 설정된다", () => { + render(); + const inputElement = screen.getByRole("textbox"); + expect(inputElement).not.toHaveAttribute("aria-required"); + }); + + test("required가 true일 때 aria-required 속성이 올바르게 설정된다", () => { + render(); + const inputElement = screen.getByRole("textbox"); + expect(inputElement).toHaveAttribute("aria-required", "true"); + }); + + test("required가 false일 때 aria-required 속성이 올바르게 설정된다", () => { + render(); + const inputElement = screen.getByRole("textbox"); + expect(inputElement).not.toHaveAttribute("aria-required"); }); - it("leadingIcon과 trailingIcon이 제공될 때 올바르게 렌더링되어야 합니다.", () => { + test("leadingIcon과 trailingIcon이 제공될 때 올바르게 렌더링되어야 한다.", () => { render(); expect(screen.getByTestId("icon-search")).toBeInTheDocument(); expect(screen.getByTestId("icon-x")).toBeInTheDocument(); }); - it("ref가 내부 input 엘리먼트로 전달되어야 합니다.", () => { + test("ref가 내부 input 엘리먼트로 전달되어야 한다.", () => { const ref = createRef(); render(); diff --git a/src/components/TextInput/TextInput.tsx b/src/components/TextInput/TextInput.tsx index 69a341ba..b9781aa9 100644 --- a/src/components/TextInput/TextInput.tsx +++ b/src/components/TextInput/TextInput.tsx @@ -25,6 +25,7 @@ export interface TextInputProps extends Omit< */ export function TextInput({ invalid, + required, className, leadingIcon, trailingIcon, @@ -57,6 +58,7 @@ export function TextInput({ ref={ref} disabled={disabled} aria-invalid={invalid ? true : undefined} + aria-required={required ? true : undefined} {...rest} /> {trailingIcon && renderIcon(trailingIcon)}