Skip to content

Implement Cypress Test For Hivechat #148

@premfrahancoder

Description

@premfrahancoder
[](Give me the full steps and process on how to solve this task as a beginner:
Create mock Hive Chat Endpoints for Hive Chat - Chat Mode
Implement Cypress Tests with Stakwork Mock Endpoints for Hivechat Flow
This will resolve issues in PR https://github.com/stakwork/sphinx-tribes-frontend/pull/1395 Allowing both PRs to be completed and closed out.

Description
Create a Cypress test for the hivechat conversation flow that uses mock Stakwork endpoints to simulate the full interaction flow. This will allow tests to run consistently in both test and local environments without depending on the actual backend services.

Requirements
Implement a comprehensive Cypress test that:

1. Mocks all necessary Stakwork API endpoints
2. Simulates the complete hivechat conversation flow
3. Verifies proper rendering of text, code, and screen artifacts
4. Tests interaction with action buttons
5. Ensures all UI elements remain properly visible and accessible

Technical Approach
1. Create Mock Response Objects
Create a dedicated fixture file with the Stakwork response objects for each stage of the conversation:
// cypress/fixtures/stakwork-responses.js
export const stakworkResponses = {
  // First response - Text artifact with action
  initialResponse: {
    value: {
      chatId: "cvljgu7paij1jhtehg9g",
      messageId: "cvljh5npaij1jhtehga0",
      response: "Ok here's the plan for you to review. Let me know what you think and we can move forwards from here.",
      sourceWebsocketId: "52a3e86e-d466-40a4-a4a8-e88938653335",
      artifacts: [
        {
          id: "111-222-333-444",
          type: "action",
          content: {
            actionText: "",
            options: [
              {
                action_type: "chat",
                option_label: "Give me feedback",
                option_response: "textbox",
                webhook: "https://jobs.stakwork.com/customer_webhooks/v1?webhook_step_name=webhook1&project_id=105376281&customer_id=7294&webhook_source=Hive"
              }
            ]
          }
        },
        {
          id: "550e8400-e29b-41d4-a716-446655440123",
          messageId: "msg_123456",
          type: "text",
          content: {
            text_type: "markdown",
            content: "Creating a plan for market domination in the sock industry requires a strategic approach that includes understanding market trends, identifying unique selling propositions, and leveraging effective marketing and distribution strategies. Here's a high-level plan:\n\n### 1. Market Research and Analysis\n- **Identify Trends:** Conduct thorough market research to understand current trends, consumer preferences, and emerging technologies in the sock industry (e.g., sustainable materials, smart socks with sensors).- **Competitive Analysis:** Analyze key competitors to understand their strengths, weaknesses, pricing models, and market positioning.\n- **Target Audience:** Define and segment your target audience (e.g., athletes, fashion-conscious consumers, eco-friendly buyers).\n\n### 2. Product Development\n\n- **Unique Selling Proposition (USP):** Develop a unique angle for your socks, such as innovative materials, customizable designs, or enhanced comfort features.\n- **Quality and Design:** Focus on high-quality materials and appealing design to differentiate your products from competitors.\n- **Sustainability:** Incorporate sustainable practices and materials to appeal to environmentally conscious consumers.\n\n### 3. Branding and Positioning\n\n- **Brand Identity:** Create a strong, recognizable brand identity that resonates with your target audience.- **Storytelling:** Use storytelling to communicate your brand values and mission, emphasizing your unique offerings and benefits.\n\n### 4. Distribution Strategy\n\n- **Online Presence:** Develop a robust online sales platform with a user-friendly e-commerce site and leverage marketplaces like Amazon and Etsy.\n- **Retail Partnerships:** Establish partnerships with retail stores and boutiques to increase visibility and accessibility.\n- **Direct-to-Consumer (DTC):** Consider a DTC model to build a direct relationship with customers, allowing for personalized marketing and feedback collection.\n\n### 5. Marketing and Promotion\n\n- **Digital Marketing:** Utilize SEO, content marketing, social media advertising, and influencer partnerships to reach a broader audience.\n\n- **Promotions and Loyalty Programs:** Offer promotions, discounts, and loyalty programs to attract and retain customers.\n\n- **Community Engagement:** Build an online community around your brand through social media interaction and user-generated content.\n\n### 6. Operational Excellence\n\n- **Supply Chain Efficiency:** Optimize your supply chain to reduce costs and improve delivery times.\n\n\n\n- **Scalability:** Develop scalable business processes to accommodate growth and expansion.\n\n### 7. Customer Experience.\n\nCan we keep going here does the page scale and scroll?\n\nCan we keep going here does the page scale and scroll?\n\nCan we keep going here does the page scale and scroll?"
          }
        }
      ]
    }
  },
  
  // Second response - Code and Screen artifacts with error dialog
  codeScreenResponse: {
    value: {
      chatId: "cvljgu7paij1jhtehg9g",
      messageId: "cvljh5npaij1jhtehga0",
      response: "Alright I've implemented the code below and fired up a VM which you can see in the screen tab.",
      sourceWebsocketId: "52a3e86e-d466-40a4-a4a8-e88938653335",
      artifacts: [
        {
          id: "111-222-333-444",
          type: "action",
          content: {
            actionText: "I detected an error do you want to see the logs?",
            options: [
              {
                action_type: "chat",
                option_label: "Give me feedback",
                option_response: "textbox",
                webhook: "https://jobs.stakwork.com/customer_webhooks/v1?webhook_step_name=webhook2&project_id=105376281&customer_id=7294&webhook_source=Hive"
              },
              {
                action_type: "button",
                option_label: "Yes",
                option_response: "Yes",
                webhook: "https://jobs.stakwork.com/customer_webhooks/v1?webhook_step_name=webhook2&project_id=105376281&customer_id=7294&webhook_source=Hive"
              },
              {
                action_type: "button",
                option_label: "Ok",
                option_response: "Ok",
                webhook: "https://jobs.stakwork.com/customer_webhooks/v1?webhook_step_name=webhook2&project_id=105376281&customer_id=7294&webhook_source```
=Hive"
              }
            ]
          }
        },
        {
          id: "550e8400-e29b-41d4-a716-446655440001",
          messageId: "msg_123456",
          type: "visual",
          content: {
            visual_type: "screen",
            url: "https://community.sphinx.chat/leaderboard"
          }
        },
        {
          id: "550e8400-e29b-41d4-a716-446655440123",
          messageId: "msg_123456",
          type: "text",
          content: {
            text_type: "code",
            code_metadata: {
              File: "stakwork/sphinx-tribes/handlers/bounty.go",
              Change: "Add GetPeopleCount handler function",
              Action: "Modify"
            },
            content: "import React, { useState, useEffect } from 'react';\nimport { Search } from 'lucide-react';\n\nconst LeaderboardPage = () => {\n  // Sample data - in a real app, this would come from an API\n  const [players, setPlayers] = useState([\n    { id: 1, rank: 1, name: \"Alex Johnson\", score: 9850, wins: 42, losses: 5, winRate: 89, team: \"Cosmic Crushers\" },\n    { id: 2, rank: 2, name: \"Taylor Swift\", score: 9720, wins: 40, losses: 8, winRate: 83, team: \"Melody Masters\" },\n    // ... additional player data\n  ]);\n\n  // Additional component code...\n};"
          }
        }
      ]
    }
  },
  
  // Third response - After clicking "Yes" on error dialog
  logsResponse: {
    value: {
      chatId: "cvljgu7paij1jhtehg9g",
      messageId: "cvljh5npaij1jhtehga0",
      response: "OK here's the logs, they should be streaming through now. \r\n\r\nI've created a code patch for you based on the logs observed, check it out and let me know if you want to apply and depoy.",
      sourceWebsocketId: "52a3e86e-d466-40a4-a4a8-e88938653335",
      artifacts: [
        {
          id: "111-222-333-444",
          type: "action",
          content: {
            actionText: "how about we fix this with patch 1523.patch",
            options: [
              {
                action_type: "chat",
                option_label: "Give me feedback",
                option_response: "textbox",
                webhook: "https://jobs.stakwork.com/customer_webhooks/v1?webhook_step_name=webhook3&project_id=105376281&customer_id=7294&webhook_source=Hive"
              },
              {
                action_type: "button",
                option_label: "Yes",
                option_response: "Yes",
                webhook: "https://jobs.stakwork.com/customer_webhooks/v1?webhook_step_name=webhook3&project_id=105376281&customer_id=7294&webhook_source=Hive"
              }
            ]
          }
        },
        {
          id: "550e8400-e29b-41d4-a716-446655440123",
          messageId: "msg_123456",
          type: "text",
          content: {
            text_type: "code",
            language: "typescript",
            content: "function applyPatch(fileSystem) {\n  const targetFile = fileSystem.getFile('/src/core/utils.js');\n  const buggyLine = targetFile.findLine('return obj.data.results.filter(x => x);');\n  targetFile.replaceLine(buggyLine, 'return obj?.data?.results?.filter(x => x) || [];');\n  console.log('PATCH-001: Fixed null reference exception in results filtering');\n}"
          }
        }
      ]
    }
  },
  
  // Fourth response - Final confirmation (empty for simplicity)
  finalResponse: {
    value: {
      chatId: "cvljgu7paij1jhtehg9g",
      messageId: "cvljh5npaij1jhtehga0",
      response: "Patch applied successfully! The fix has been deployed.",
      sourceWebsocketId: "52a3e86e-d466-40a4-a4a8-e88938653335",
    }
  }
};
  1. Create Custom Command for Mocking the Stakwork API
    Add a custom command that sets up all the necessary interceptions for the Stakwork API:
// cypress/support/commands.js
import { stakworkResponses } from '../fixtures/stakwork-responses';

Cypress.Commands.add('mockStakworkAPI', () => {
  // Counter to track which response to return for POST /hivechat/response
  let responseCounter = 0;
  
  // Mock POST requests to /hivechat/response
  cy.intercept('POST', '**/hivechat/response', (req) => {
    let mockResponse;
    
    // Determine which mock response to return based on the counter
    if (responseCounter === 0) {
      mockResponse = stakworkResponses.initialResponse;
    } else if (responseCounter === 1) {
      mockResponse = stakworkResponses.codeScreenResponse;
    } else if (responseCounter === 2) {
      mockResponse = stakworkResponses.logsResponse;
    } else {
      mockResponse = stakworkResponses.finalResponse;
    }
    
    responseCounter++;
    req.reply({ statusCode: 200, body: mockResponse });
  }).as('hivechatResponse');
  
  // Mock POST requests to button action webhooks
  cy.intercept('POST', '**/customer_webhooks/**', {
    statusCode: 200,
    body: { success: true }
  }).as('webhookAction');
});
  1. Implement the Cypress Test
    Create a test file that uses the mock endpoints to test the hivechat flow:
// cypress/e2e/hivechat_flow.cy.js
describe('Hivechat Conversation Flow with Mock Stakwork API', () => {
  beforeEach(() => {
    // Set up mock API responses
    cy.mockStakworkAPI();
    
    // Login with test user
    cy.login('testuser');
    
    // Navigate to hivechat view with placeholder workspace/chat IDs
    // These should be replaced with actual or dynamically generated IDs in your implementation
    cy.visit('/workspace/workspaceID/hivechat/chatID');
    cy.wait(2000); // Wait for page to fully load
  });

  it('Should complete the hivechat conversation flow with all artifacts rendering correctly', () => {
    // 1. Select Chat mode in top right
    cy.get('[data-testid="chat-mode-button"]').click();
    cy.wait(500);
    
    // 2. Submit the initial message with @ticket mention
    const initialMessage = 'Lets create a new @ticket to update the leaderboard';
    cy.get('[data-testid="message-input"]')
      .should('be.visible')
      .type(initialMessage);
    cy.get('[data-testid="send-message-button"]').click();
    
    // Wait for the first hivechat response
    cy.wait('@hivechatResponse').then((interception) => {
      // Verify response contains expected text artifact
      expect(interception.response.body.value.artifacts).to.have.length.at.least(1);
      expect(interception.response.body.value.artifacts[1].type).to.equal('text');
    });
    
    // 3. Verify Text artifact is rendered with notification
    cy.get('[data-testid="artifact-tab-Text"]')
      .should('be.visible')
      .and('have.attr', 'data-notification', 'true');
    
    cy.get('[data-testid="artifact-content-text"]')
      .should('be.visible')
      .should('contain', 'Market Research and Analysis');
    
    // Take screenshot to verify text artifact rendering
    cy.screenshot('text-artifact-rendering');
    
    // 4. Send follow-up message "Yes sounds good"
    cy.get('[data-testid="message-input"]')
      .should('be.visible')
      .clear()
      .type('Yes sounds good');
    cy.get('[data-testid="send-message-button"]').click();
    
    // Wait for the second hivechat response
    cy.wait('@hivechatResponse').then((interception) => {
      // Verify response contains code and screen artifacts
      expect(interception.response.body.value.artifacts).to.have.length.at.least(2);
      const artifactTypes = interception.response.body.value.artifacts.map(a => a.type);
      expect(artifactTypes).to.include('action');
      expect(artifactTypes).to.include('visual');
      expect(artifactTypes).to.include('text');
    });
    
    // 5. Verify Code artifact is rendered
    cy.get('[data-testid="artifact-tab-Code"]')
      .should('be.visible')
      .and('have.attr', 'data-notification', 'true')
      .click();
    
    cy.get('[data-testid="artifact-content-code"]')
      .should('be.visible')
      .should('contain', 'LeaderboardPage');
    
    // 6. Verify Screen artifact is rendered
    cy.get('[data-testid="artifact-tab-Screen"]')
      .should('be.visible')
      .and('have.attr', 'data-notification', 'true')
      .click();
    
    cy.get('[data-testid="artifact-content-screen"]')
      .should('be.visible');
    
    // 7. Verify action dialog is displayed with error message
    cy.get('[data-testid="action-dialog"]')
      .should('be.visible')
      .should('contain', 'I detected an error do you want to see the logs?');
    
    // Screenshot the action dialog
    cy.screenshot('action-dialog');
    
    // 8. Click "Yes" button in action dialog
    cy.get('[data-testid="action-dialog"]')
      .contains('Yes')
      .click();
    
    // Wait for the third hivechat response
    cy.wait('@hivechatResponse').then((interception) => {
      // Verify response contains logs and patch information
      expect(interception.response.body.value.artifacts).to.have.length.at.least(1);
      expect(interception.response.body.value.response).to.include('logs');
    });
    
    // 9. Verify the logs and patch code are displayed
    cy.get('[data-testid="artifact-content-code"]')
      .should('be.visible')
      .should('contain', 'applyPatch');
    
    // 10. Verify the second action dialog
    cy.get('[data-testid="action-dialog"]')
      .should('be.visible')
      .should('contain', 'patch 1523.patch');
    
    // 11. Click "Yes" button in the second action dialog
    cy.get('[data-testid="action-dialog"]')
      .contains('Yes')
      .click();
    
    // Wait for the final hivechat response
    cy.wait('@hivechatResponse');
    
    // 12. Verify the conversation completed successfully
    cy.get('[data-testid="message-history"]')
      .should('contain', 'Patch applied successfully');
    
    // Take final screenshot
    cy.screenshot('completed-flow');
  });
  
  afterEach(() => {
    // Clean up after test
    cy.logout();
  });
});


  1. Configure Environment for Mock vs Real Endpoints
    Create environment configurations to toggle between mock and real endpoints:
// cypress.config.js
const { defineConfig } = require("cypress");

module.exports = defineConfig({
  e2e: {
    setupNodeEvents(on, config) {
      // implement node event listeners here
    },
    baseUrl: 'http://localhost:3007',
    env: {
      useMocks: true, // Toggle this to false to use real endpoints
    }
  },
});

Then update your custom command to check the environment setting:

Cypress.Commands.add('mockStakworkAPI', () => {
  // Skip mocking if useMocks is false
  if (Cypress.env('useMocks') === false) {
    return;
  }

  // Set up mocks as before...
});

  1. Create Alternative Versions for Different Environments
    Create separate config files for different environments:
// cypress.config.local.js
const baseConfig = require('./cypress.config');

module.exports = {
  ...baseConfig,
  e2e: {
    ...baseConfig.e2e,
    baseUrl: 'http://localhost:3007',
    env: {
      useMocks: true,
    }
  }
};

// cypress.config.test.js
const baseConfig = require('./cypress.config');

module.exports = {
  ...baseConfig,
  e2e: {
    ...baseConfig.e2e,
    baseUrl: 'https://test.yourapp.com',
    env: {
      useMocks: true,
    }
  }
};

// cypress.config.prod.js
const baseConfig = require('./cypress.config');

module.exports = {
  ...baseConfig,
  e2e: {
    ...baseConfig.e2e,
    baseUrl: 'https://yourapp.com',
    env: {
      useMocks: false, // Use real endpoints in prod for monitoring tests
    }
  }
};)

Testing Against Real Services (Optional)
For scenarios where you want to test against real services but in a controlled manner:

Create a test-specific endpoint in your backend that returns predefined responses
Update your Cypress tests to use this endpoint instead of mocks when useMocks is false

// Backend route example (Express.js)
app.post('/test-api/hivechat/response', (req, res) => {
  const stage = req.query.stage || '1';
  const testResponses = require('./test-responses.json');
  res.json(testResponses[`stage${stage}`]);
});

Then update your Cypress command:

Cypress.Commands.add('setupHivechatEndpoints', () => {
  if (Cypress.env('useMocks')) {
    // Set up mocks as before
  } else {
    // Use test-specific real endpoint
    cy.intercept('POST', '**/hivechat/response', (req) => {
      const originalUrl = req.url;
      req.url = `${Cypress.config('baseUrl')}/test-api/hivechat/response?stage=${responseCounter}`;
      responseCounter++;
    }).as('hivechatResponse');
  }
});

Running the Tests
Add npm scripts to run tests with different configurations:

// package.json
{
  "scripts": {
    "cy:open": "cypress open",
    "cy:run": "cypress run",
    "cy:run:local": "cypress run --config-file cypress.config.local.js",
    "cy:run:test": "cypress run --config-file cypress.config.test.js",
    "cy:run:prod": "cypress run --config-file cypress.config.prod.js"
  }
}

Acceptance Criteria:
Cypress test successfully mocks all Stakwork API endpoints needed for the hivechat flow in Chat Mode
Test verifies the initial text artifact is properly rendered with notification
Test verifies code and screen artifacts are properly rendered with notifications
Test successfully interacts with action dialogs (clicking "Yes" buttons)
Test verifies all UI elements remain visible and properly rendered throughout the flow
Test runs successfully in both local and test environments
Mock responses match the format of actual Stakwork API responses
Environment configuration allows toggling betwee)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions