From 70c99e9b4e3259e97edd4a2e8a2e9afa0992a76d Mon Sep 17 00:00:00 2001 From: Dagger Date: Tue, 15 Jul 2025 03:29:32 +0000 Subject: [PATCH 1/3] add_3d_globe_visualization_showing_language_locales_with_backend_locale_support_1752550160 --- greetings.json | 36 ++++---- main.go | 3 +- main_test.go | 4 +- website/index.html | 219 ++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 240 insertions(+), 22 deletions(-) diff --git a/greetings.json b/greetings.json index a9934af..8f054c7 100644 --- a/greetings.json +++ b/greetings.json @@ -1,19 +1,19 @@ [ - { "language": "english", "greeting": "Hello, World!" }, - { "language": "british", "greeting": "Hello, World! Cheers!" }, - { "language": "french", "greeting": "Bonjour, Monde !" }, - { "language": "italian", "greeting": "Ciao, Mondo!" }, - { "language": "spanish", "greeting": "¡Hola, Mundo!" }, - { "language": "german", "greeting": "Hallo, Welt!" }, - { "language": "mandarin", "greeting": "你好,世界!" }, - { "language": "hindi", "greeting": "नमस्ते दुनिया!" }, - { "language": "arabic", "greeting": "مرحبا بالعالم!" }, - { "language": "bengali", "greeting": "ওহে বিশ্ব!" }, - { "language": "russian", "greeting": "Привет, мир!" }, - { "language": "portuguese", "greeting": "Olá, Mundo!" }, - { "language": "urdu", "greeting": "ہیلو، دنیا!" }, - { "language": "indonesian", "greeting": "Halo Dunia!" }, - { "language": "japanese", "greeting": "こんにちは世界!" }, - { "language": "marathi", "greeting": "नमस्कार जग!" }, - { "language": "telugu", "greeting": "హలో ప్రపంచం!" } -] + { "language": "english", "greeting": "Hello, World!", "locale": "US" }, + { "language": "british", "greeting": "Hello, World! Cheers!", "locale": "GB" }, + { "language": "french", "greeting": "Bonjour, Monde !", "locale": "FR" }, + { "language": "italian", "greeting": "Ciao, Mondo!", "locale": "IT" }, + { "language": "spanish", "greeting": "¡Hola, Mundo!", "locale": "ES" }, + { "language": "german", "greeting": "Hallo, Welt!", "locale": "DE" }, + { "language": "mandarin", "greeting": "你好,世界!", "locale": "CN" }, + { "language": "hindi", "greeting": "नमस्ते दुनिया!", "locale": "IN" }, + { "language": "arabic", "greeting": "مرحبا بالعالم!", "locale": "SA" }, + { "language": "bengali", "greeting": "ওহে বিশ্ব!", "locale": "BD" }, + { "language": "russian", "greeting": "Привет, мир!", "locale": "RU" }, + { "language": "portuguese", "greeting": "Olá, Mundo!", "locale": "BR" }, + { "language": "urdu", "greeting": "ہیلو، دنیا!", "locale": "PK" }, + { "language": "indonesian", "greeting": "Halo Dunia!", "locale": "ID" }, + { "language": "japanese", "greeting": "こんにちは世界!", "locale": "JP" }, + { "language": "marathi", "greeting": "नमस्कार जग!", "locale": "IN" }, + { "language": "telugu", "greeting": "హలో ప్రపంచం!", "locale": "IN" } +] \ No newline at end of file diff --git a/main.go b/main.go index f3dd57c..f914f0e 100644 --- a/main.go +++ b/main.go @@ -19,6 +19,7 @@ var greetingsJson []byte type Greeting struct { Language string `json:"language"` Greeting string `json:"greeting"` + Locale string `json:"locale"` } func main() { @@ -75,7 +76,7 @@ func main() { } func FormatResponse(greeting *Greeting) string { - return fmt.Sprintf("{\"greeting\":\"%s\"}", greeting.Greeting) + return fmt.Sprintf("{\"greeting\":\"%s\",\"locale\":\"%s\"}", greeting.Greeting, greeting.Locale) } func SelectGreeting(greetings []*Greeting, language string) (*Greeting, error) { diff --git a/main_test.go b/main_test.go index e05e339..5fd1ac4 100644 --- a/main_test.go +++ b/main_test.go @@ -20,6 +20,7 @@ func TestSelectGreeting(t *testing.T) { english := &Greeting{ Greeting: "Hello, World!", Language: "english", + Locale: "US", } // Test with a language @@ -44,8 +45,9 @@ func TestFormatResponse(t *testing.T) { g := &Greeting{ Greeting: "Hello, World!", Language: "english", + Locale: "US", } formatted := FormatResponse(g) - assert.Equal(t, "{\"greeting\":\"Hello, World!\"}", formatted) + assert.Equal(t, "{\"greeting\":\"Hello, World!\",\"locale\":\"US\"}", formatted) } diff --git a/website/index.html b/website/index.html index 7e6f858..6cac002 100644 --- a/website/index.html +++ b/website/index.html @@ -5,6 +5,27 @@ Greetings App + +
@@ -12,19 +33,213 @@

Greetings Daggernauts

Click the button to see a greeting!
+ +
From 139e01dc4092394c0123302f4f1de6c6c1e1be74 Mon Sep 17 00:00:00 2001 From: Dagger Date: Tue, 15 Jul 2025 03:34:53 +0000 Subject: [PATCH 2/3] add_3d_globe_visualization_showing_language_locales_with_backend_locale_support_1752550160 --- .dagger/prompts/assignment.md | 34 ++++++++++ CONTRIBUTING.md | 35 +++++++++-- greetings.json | 2 +- main.go | 13 ++++ website/cypress/e2e/greeting_test.cy.ts | 84 +++++++++++++++++++++++++ website/index.html | 2 +- 6 files changed, 164 insertions(+), 6 deletions(-) diff --git a/.dagger/prompts/assignment.md b/.dagger/prompts/assignment.md index 6a2d489..551f0db 100644 --- a/.dagger/prompts/assignment.md +++ b/.dagger/prompts/assignment.md @@ -12,6 +12,40 @@ You are a programmer working on the Greetings API project Here is your assignment: $assignment +## Implementation Guidelines + +### API Endpoints +- Always maintain consistency with existing API endpoints +- If adding new endpoints, ensure they follow the established pattern +- The `/random` endpoint should be preserved for backward compatibility +- When modifying API responses, update both the Go structs and the JSON formatting + +### Frontend Development +- When adding new UI components, ensure they are initially hidden and only show after user interaction +- Always include proper error handling for API calls +- For complex visualizations (like the globe), use CDN libraries but consider performance implications +- Test both successful API responses and error cases + +### Testing Requirements +- Always update existing tests when changing API response formats +- Add comprehensive test coverage for new features, including: + - UI component visibility changes + - API response validation + - Error handling scenarios + - Edge cases (like unknown locale codes) +- Use Cypress intercepts for predictable testing of API-dependent features + +### Data Files +- Always include trailing newlines in JSON files +- When adding new fields to data structures, ensure they are properly reflected in both the data file and the Go structs +- Use appropriate locale codes (ISO country codes) for international features + +### Code Quality +- Follow established patterns in the codebase +- Include proper error handling and graceful degradation +- Consider accessibility when adding new UI features +- Clean up resources (like animation frames) when appropriate + ## Constraints - You have access to a workspace with the code and the tests - The workspace has tools to let you read and write the code as well as run the tests diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index aad8504..86ccd9e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -64,8 +64,9 @@ greetings-api/ - **Language**: Go - **Framework**: Gorilla Mux for routing, CORS middleware -- **Structure**: Simple REST API with two endpoints: +- **Structure**: Simple REST API with endpoints: - `GET /` - Returns a random greeting + - `GET /random` - Returns a random greeting (alternative endpoint) - `GET /{language}` - Returns a greeting in the specified language - **Data**: Greetings are stored in `greetings.json` and embedded in the binary - **Testing**: Uses `gotest.tools` for unit tests @@ -76,6 +77,7 @@ greetings-api/ - **Testing**: Cypress for end-to-end tests - **Linting**: ESLint with TypeScript support - **Build**: Managed through Dagger modules +- **Visualization**: Uses Three.js for 3D globe rendering ### CI/CD Architecture @@ -154,6 +156,27 @@ npm run lint - **TypeScript**: Follow the ESLint configuration in the project - **Commits**: Use clear, descriptive commit messages +### API Development Guidelines + +- **Endpoint Consistency**: Maintain consistency with existing API endpoints +- **Backward Compatibility**: Preserve existing endpoints when adding new ones +- **Response Format**: When modifying API responses, update both Go structs and JSON formatting +- **Error Handling**: Always include proper error handling and meaningful error messages + +### Frontend Development Guidelines + +- **Progressive Enhancement**: New UI components should be initially hidden and show after user interaction +- **Error Handling**: Always include proper error handling for API calls +- **Performance**: Consider performance implications when adding external libraries +- **Accessibility**: Include accessibility features where appropriate +- **Resource Management**: Clean up resources (like animation frames) when components are no longer needed + +### Data File Guidelines + +- **File Format**: Always include trailing newlines in JSON files +- **Data Consistency**: When adding new fields, ensure they're reflected in both data files and Go structs +- **Internationalization**: Use appropriate locale codes (ISO country codes) for international features + ### Documentation - **Developer Docs**: Update CONTRIBUTING.md with any architectural changes @@ -161,9 +184,12 @@ npm run lint ### Testing Requirements -- All new Go code should include unit tests -- Frontend changes should not break existing E2E tests -- Run the full test suite before submitting PRs: `dagger call check` +- **Unit Tests**: All new Go code should include unit tests +- **API Tests**: Test both successful API responses and error cases +- **Frontend Tests**: Use Cypress intercepts for predictable testing of API-dependent features +- **Comprehensive Coverage**: Test UI component visibility, API response validation, error handling, and edge cases +- **Test Updates**: Update existing tests when changing API response formats +- **Full Suite**: Run the complete test suite before submitting PRs: `dagger call check` ### Pull Request Process @@ -190,6 +216,7 @@ npm run lint - Clear description of changes - Reference to any related issues - Screenshots if UI changes are involved + - Confirmation that all tests pass ## Getting Help diff --git a/greetings.json b/greetings.json index 8f054c7..27d13e4 100644 --- a/greetings.json +++ b/greetings.json @@ -16,4 +16,4 @@ { "language": "japanese", "greeting": "こんにちは世界!", "locale": "JP" }, { "language": "marathi", "greeting": "नमस्कार जग!", "locale": "IN" }, { "language": "telugu", "greeting": "హలో ప్రపంచం!", "locale": "IN" } -] \ No newline at end of file +] diff --git a/main.go b/main.go index f914f0e..ddb3b13 100644 --- a/main.go +++ b/main.go @@ -44,6 +44,19 @@ func main() { } }).Methods("GET") + router.HandleFunc("/random", func(w http.ResponseWriter, r *http.Request) { + fmt.Printf("got /random request from %s\n", r.RemoteAddr) + w.Header().Set("Content-Type", "application/json") + greeting, err := SelectGreeting(greetings, "random") + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + } + _, err = w.Write([]byte(FormatResponse(greeting))) + if err != nil { + panic(err) + } + }).Methods("GET") + router.HandleFunc("/{language}", func(w http.ResponseWriter, r *http.Request) { language := mux.Vars(r)["language"] fmt.Printf("got /{language} request from %s\n", r.RemoteAddr) diff --git a/website/cypress/e2e/greeting_test.cy.ts b/website/cypress/e2e/greeting_test.cy.ts index cb69949..8f3f62d 100644 --- a/website/cypress/e2e/greeting_test.cy.ts +++ b/website/cypress/e2e/greeting_test.cy.ts @@ -18,4 +18,88 @@ describe("Greetings API", () => { "Click the button to see a greeting!", ); }); + + it("should display the globe container after clicking the button", () => { + cy.visit("/"); + + // Initially, globe container should be hidden + cy.get("#globeContainer").should("not.be.visible"); + cy.get("#locationInfo").should("not.be.visible"); + + // Click the button to get a greeting + cy.get("#randomGreetingButton").click(); + + // Wait for the response and check that globe and location info are visible + cy.get("#globeContainer").should("be.visible"); + cy.get("#locationInfo").should("be.visible"); + }); + + it("should display location information correctly", () => { + cy.visit("/"); + + // Mock the API response to ensure predictable testing + cy.intercept('GET', 'http://localhost:8080/random', { + statusCode: 200, + body: { + greeting: "Hello, World!", + locale: "US" + } + }).as('getGreeting'); + + // Click the button + cy.get("#randomGreetingButton").click(); + + // Wait for the API call + cy.wait('@getGreeting'); + + // Check that location info shows the correct country + cy.get("#locationInfo").should("contain", "United States"); + }); + + it("should handle API response with locale field", () => { + cy.visit("/"); + + // Mock an API response with locale + cy.intercept('GET', 'http://localhost:8080/random', { + statusCode: 200, + body: { + greeting: "Bonjour, Monde !", + locale: "FR" + } + }).as('getFrenchGreeting'); + + // Click the button + cy.get("#randomGreetingButton").click(); + + // Wait for the API call + cy.wait('@getFrenchGreeting'); + + // Check that the greeting is displayed + cy.get("#greetingDisplay").should("contain", "Bonjour, Monde !"); + + // Check that location info shows France + cy.get("#locationInfo").should("contain", "France"); + }); + + it("should handle unknown locale gracefully", () => { + cy.visit("/"); + + // Mock an API response with an unknown locale + cy.intercept('GET', 'http://localhost:8080/random', { + statusCode: 200, + body: { + greeting: "Hello, World!", + locale: "XX" + } + }).as('getUnknownLocale'); + + // Click the button + cy.get("#randomGreetingButton").click(); + + // Wait for the API call + cy.wait('@getUnknownLocale'); + + // Check that location info shows fallback text + cy.get("#locationInfo").should("contain", "Location: XX"); + }); }); diff --git a/website/index.html b/website/index.html index 6cac002..00e4fa7 100644 --- a/website/index.html +++ b/website/index.html @@ -227,7 +227,7 @@

Greetings Daggernauts

async function getRandomGreeting() { try { - const response = await fetch('http://localhost:8080/'); + const response = await fetch('http://localhost:8080/random'); const data = await response.json(); document.getElementById('greetingDisplay').textContent = data.greeting; From 4421fc5a493bb79e6dbb4266a3eadefb46a3e399 Mon Sep 17 00:00:00 2001 From: kpenfound Date: Tue, 15 Jul 2025 00:04:46 -0400 Subject: [PATCH 3/3] fix cypress test by mocking random endpoint Signed-off-by: kpenfound --- website/cypress/e2e/greeting_test.cy.ts | 71 ++++++++++++++----------- 1 file changed, 40 insertions(+), 31 deletions(-) diff --git a/website/cypress/e2e/greeting_test.cy.ts b/website/cypress/e2e/greeting_test.cy.ts index 8f3f62d..ea77cf4 100644 --- a/website/cypress/e2e/greeting_test.cy.ts +++ b/website/cypress/e2e/greeting_test.cy.ts @@ -21,14 +21,23 @@ describe("Greetings API", () => { it("should display the globe container after clicking the button", () => { cy.visit("/"); - + + // Mock the API response to ensure predictable testing + cy.intercept("GET", "http://localhost:8080/random", { + statusCode: 200, + body: { + greeting: "Hello, World!", + locale: "US", + }, + }).as("getGreeting"); + // Initially, globe container should be hidden cy.get("#globeContainer").should("not.be.visible"); cy.get("#locationInfo").should("not.be.visible"); - + // Click the button to get a greeting cy.get("#randomGreetingButton").click(); - + // Wait for the response and check that globe and location info are visible cy.get("#globeContainer").should("be.visible"); cy.get("#locationInfo").should("be.visible"); @@ -36,69 +45,69 @@ describe("Greetings API", () => { it("should display location information correctly", () => { cy.visit("/"); - + // Mock the API response to ensure predictable testing - cy.intercept('GET', 'http://localhost:8080/random', { + cy.intercept("GET", "http://localhost:8080/random", { statusCode: 200, body: { greeting: "Hello, World!", - locale: "US" - } - }).as('getGreeting'); - + locale: "US", + }, + }).as("getGreeting"); + // Click the button cy.get("#randomGreetingButton").click(); - + // Wait for the API call - cy.wait('@getGreeting'); - + cy.wait("@getGreeting"); + // Check that location info shows the correct country cy.get("#locationInfo").should("contain", "United States"); }); it("should handle API response with locale field", () => { cy.visit("/"); - + // Mock an API response with locale - cy.intercept('GET', 'http://localhost:8080/random', { + cy.intercept("GET", "http://localhost:8080/random", { statusCode: 200, body: { greeting: "Bonjour, Monde !", - locale: "FR" - } - }).as('getFrenchGreeting'); - + locale: "FR", + }, + }).as("getFrenchGreeting"); + // Click the button cy.get("#randomGreetingButton").click(); - + // Wait for the API call - cy.wait('@getFrenchGreeting'); - + cy.wait("@getFrenchGreeting"); + // Check that the greeting is displayed cy.get("#greetingDisplay").should("contain", "Bonjour, Monde !"); - + // Check that location info shows France cy.get("#locationInfo").should("contain", "France"); }); it("should handle unknown locale gracefully", () => { cy.visit("/"); - + // Mock an API response with an unknown locale - cy.intercept('GET', 'http://localhost:8080/random', { + cy.intercept("GET", "http://localhost:8080/random", { statusCode: 200, body: { greeting: "Hello, World!", - locale: "XX" - } - }).as('getUnknownLocale'); - + locale: "XX", + }, + }).as("getUnknownLocale"); + // Click the button cy.get("#randomGreetingButton").click(); - + // Wait for the API call - cy.wait('@getUnknownLocale'); - + cy.wait("@getUnknownLocale"); + // Check that location info shows fallback text cy.get("#locationInfo").should("contain", "Location: XX"); });