+
- String Calculator
+ String Calculator
- Enter numbers
-
-
);
};
diff --git a/src/stringCalculator.test.ts b/src/stringCalculator.test.ts
index e69de29..6038587 100644
--- a/src/stringCalculator.test.ts
+++ b/src/stringCalculator.test.ts
@@ -0,0 +1,54 @@
+import { describe, it, expect } from "vitest";
+import { add } from "./stringCalculator";
+
+describe("String Calculator", () => {
+ it("should return 0 for an empty string", () => {
+ expect(add("")).toBe(0);
+ });
+
+ it("should return the number itself for a single number", () => {
+ expect(add("1")).toBe(1);
+ expect(add("5")).toBe(5);
+ });
+
+ it("should return the sum of two comma-separated numbers", () => {
+ expect(add("1,2")).toBe(3);
+ expect(add("10,20")).toBe(30);
+ });
+
+ it("should return the sum of multiple comma-separated numbers", () => {
+ expect(add("1,2,3")).toBe(6);
+ expect(add("1,2,3,4,5")).toBe(15);
+ });
+
+ it("should handle newlines as delimiters", () => {
+ expect(add("1\n2,3")).toBe(6);
+ expect(add("1\n2\n3")).toBe(6);
+ });
+
+ it("should handle custom delimiters", () => {
+ expect(add("//;\n1;2")).toBe(3);
+ expect(add("//|\n1|2|3")).toBe(6);
+ });
+
+ it("should throw error for negative numbers", () => {
+ expect(() => add("1,-2,3")).toThrow("Negative numbers not allowed: -2");
+ expect(() => add("-1,-2")).toThrow("Negative numbers not allowed: -1, -2");
+ });
+
+ it("should throw error for invalid inputs (non-numeric strings)", () => {
+ expect(() => add("1,a,2")).toThrow("Invalid input: a");
+ expect(() => add("1,xyz,2")).toThrow("Invalid input: xyz");
+ expect(() => add("1,abc,def")).toThrow("Invalid input: abc, def");
+ });
+
+ it("should handle empty values between delimiters", () => {
+ expect(add("1,,2")).toBe(3);
+ expect(add("1, ,2")).toBe(3);
+ });
+
+ it("should handle whitespace in numbers", () => {
+ expect(add("1, 2, 3")).toBe(6);
+ expect(add(" 1 , 2 ")).toBe(3);
+ });
+});
diff --git a/src/stringCalculator.ts b/src/stringCalculator.ts
index e69de29..6b1954c 100644
--- a/src/stringCalculator.ts
+++ b/src/stringCalculator.ts
@@ -0,0 +1,57 @@
+export function add(numbers: string): number {
+ if (!numbers) return 0;
+
+ let delimiter = /,|\n/;
+ let numString = numbers;
+
+ // Check for custom delimiter
+ if (numbers.startsWith('//')) {
+ const delimiterEnd = numbers.indexOf('\n');
+ const customDelimiter = numbers.substring(2, delimiterEnd);
+ // Escape special regex characters
+ const escapedDelimiter = customDelimiter.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
+ delimiter = new RegExp(escapedDelimiter);
+ numString = numbers.substring(delimiterEnd + 1);
+ }
+
+ // Split by delimiter and process each string
+ const numStrings = numString.split(delimiter);
+ const numArray: number[] = [];
+ const negatives: number[] = [];
+ const invalidInputs: string[] = [];
+
+ for (const str of numStrings) {
+ const trimmed = str.trim();
+
+ // Skip empty strings
+ if (!trimmed) continue;
+
+ // Parse the string to number
+ const num = parseInt(trimmed, 10);
+
+ // Check if it's a valid number
+ if (isNaN(num)) {
+ invalidInputs.push(trimmed);
+ continue;
+ }
+
+ // Check for negative numbers
+ if (num < 0) {
+ negatives.push(num);
+ } else {
+ numArray.push(num);
+ }
+ }
+
+ // Throw error if invalid inputs found
+ if (invalidInputs.length > 0) {
+ throw new Error(`Invalid input: ${invalidInputs.join(', ')}`);
+ }
+
+ // Throw error if negative numbers found
+ if (negatives.length > 0) {
+ throw new Error(`Negative numbers not allowed: ${negatives.join(', ')}`);
+ }
+
+ return numArray.reduce((sum, num) => sum + num, 0);
+}
\ No newline at end of file
diff --git a/src/styles.css b/src/styles.css
new file mode 100644
index 0000000..ef50322
--- /dev/null
+++ b/src/styles.css
@@ -0,0 +1,41 @@
+/* Global Styles for Accessibility */
+
+/* Ensure visible focus indicators for keyboard navigation */
+*:focus {
+ outline: 3px solid #4A90E2;
+ outline-offset: 2px;
+}
+
+/* High contrast focus for better visibility */
+button:focus,
+textarea:focus,
+input:focus {
+ outline: 3px solid #0066cc;
+ outline-offset: 2px;
+}
+
+/* Ensure :focus-visible for modern browsers */
+*:focus:not(:focus-visible) {
+ outline: none;
+}
+
+*:focus-visible {
+ outline: 3px solid #4A90E2;
+ outline-offset: 2px;
+}
+
+/* Skip to main content link for screen readers */
+.skip-link {
+ position: absolute;
+ top: -40px;
+ left: 0;
+ background: #000;
+ color: #fff;
+ padding: 8px;
+ text-decoration: none;
+ z-index: 100;
+}
+
+.skip-link:focus {
+ top: 0;
+}
diff --git a/src/test/debug.js b/src/test/debug.js
new file mode 100644
index 0000000..bdbd21e
--- /dev/null
+++ b/src/test/debug.js
@@ -0,0 +1,2 @@
+const matchers = require("@testing-library/jest-dom/matchers");
+console.log("Matchers:", Object.keys(matchers));
diff --git a/src/test/setup.test.tsx b/src/test/setup.test.tsx
new file mode 100644
index 0000000..0b1dd8d
--- /dev/null
+++ b/src/test/setup.test.tsx
@@ -0,0 +1,20 @@
+import { describe, it, expect } from "vitest";
+import { render, screen } from "@testing-library/react";
+
+describe("Setup Verification", () => {
+ it("should verify testing environment is configured correctly", () => {
+ const TestComponent = () => (
+
Test Setup Works
+ );
+ render(
);
+
+ // Verify element exists and has correct content
+ const element = screen.getByTestId("test-element");
+ expect(element).toBeTruthy();
+ expect(element.textContent).toBe("Test Setup Works");
+
+ // Verify we can query by role
+ const divElement = screen.getByText("Test Setup Works");
+ expect(divElement.tagName).toBe("DIV");
+ });
+});
diff --git a/src/test/setup.ts b/src/test/setup.ts
new file mode 100644
index 0000000..49a588c
--- /dev/null
+++ b/src/test/setup.ts
@@ -0,0 +1,8 @@
+// Setup file for Vitest + React Testing Library
+import { afterEach } from "vitest";
+import { cleanup } from "@testing-library/react";
+
+// Cleanup after each test
+afterEach(() => {
+ cleanup();
+});
diff --git a/src/test/tsconfig.json b/src/test/tsconfig.json
new file mode 100644
index 0000000..9e9b29c
--- /dev/null
+++ b/src/test/tsconfig.json
@@ -0,0 +1,6 @@
+{
+ "compilerOptions": {
+ "jsx": "react",
+ "types": ["vitest/globals", "@testing-library/jest-dom"]
+ }
+}
diff --git a/vite.config.ts b/vite.config.ts
index 4f312ac..907acf0 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -1,6 +1,17 @@
-import { defineConfig } from 'vite';
-import react from '@vitejs/plugin-react-swc';
+///
+import { defineConfig } from "vite";
+import react from "@vitejs/plugin-react-swc";
export default defineConfig({
plugins: [react()],
+ test: {
+ globals: true,
+ environment: "jsdom",
+ setupFiles: "./src/test/setup.ts",
+ environmentOptions: {
+ jsdom: {
+ resources: "usable",
+ },
+ },
+ },
});