diff --git a/.forgejo/workflows/ci-cd-beta.yml b/.forgejo/workflows/ci-cd-beta.yml index ab6f2ee..8c89687 100644 --- a/.forgejo/workflows/ci-cd-beta.yml +++ b/.forgejo/workflows/ci-cd-beta.yml @@ -8,6 +8,7 @@ on: jobs: build-and-test: runs-on: ubuntu-latest + container: node:22 steps: - name: Checkout Code uses: actions/checkout@v3 @@ -33,6 +34,7 @@ jobs: deploy-beta: needs: build-and-test runs-on: ubuntu-latest + container: node:22 steps: - name: Download Build Artifact uses: actions/download-artifact@v3 @@ -40,10 +42,12 @@ jobs: name: dist path: dist - - name: Deploy to Beta via FTP - env: - FTP_USERNAME: ${{ secrets.BETA_FTP_USERNAME }} - FTP_PASSWORD: ${{ secrets.BETA_FTP_PASSWORD }} - FTP_HOST: ${{ secrets.FTP_HOST }} + - name: Install AWS CLI run: | - lftp -d -u "$FTP_USERNAME","$FTP_PASSWORD" -e "set ssl:verify-certificate no; set ftp:ssl-allow yes; set ftp:ssl-force true; set ftp:ssl-protect-data true; set ftp:passive-mode true; mirror -R dist/ ./; quit" $FTP_HOST + apt-get update && apt-get install -y awscli + + - name: Deploy to S3 + run: aws s3 sync dist/ s3://${{ secrets.AWS_S3_BUCKET_BETA }} --delete + + - name: Invalidate CloudFront + run: aws cloudfront create-invalidation --distribution-id E2AYEICRZKJ9TM --paths "/*" diff --git a/.forgejo/workflows/ci-cd-main.yml b/.forgejo/workflows/ci-cd-main.yml index 6f92c52..a124278 100644 --- a/.forgejo/workflows/ci-cd-main.yml +++ b/.forgejo/workflows/ci-cd-main.yml @@ -1,4 +1,4 @@ -name: Production CI/CD Pipeline +name: Beta CI/CD Pipeline on: push: @@ -8,6 +8,7 @@ on: jobs: build-and-test: runs-on: ubuntu-latest + container: node:22 steps: - name: Checkout Code uses: actions/checkout@v3 @@ -30,11 +31,10 @@ jobs: name: dist path: dist/ - deploy-prod: + deploy-beta: needs: build-and-test runs-on: ubuntu-latest - environment: - name: production + container: node:22 steps: - name: Download Build Artifact uses: actions/download-artifact@v3 @@ -42,10 +42,12 @@ jobs: name: dist path: dist - - name: Deploy to Production via FTP - env: - FTP_USERNAME: ${{ secrets.FTP_USERNAME }} - FTP_PASSWORD: ${{ secrets.FTP_PASSWORD }} - FTP_HOST: ${{ secrets.FTP_HOST }} + - name: Install AWS CLI run: | - lftp -d -u "$FTP_USERNAME","$FTP_PASSWORD" -e "set ssl:verify-certificate no; set ftp:ssl-allow yes; set ftp:ssl-force true; set ftp:ssl-protect-data true; set ftp:passive-mode true; mirror -R dist/ ./; quit" $FTP_HOST + apt-get update && apt-get install -y awscli + + - name: Deploy to S3 + run: aws s3 sync dist/ s3://partnerproductionsite --delete + + - name: Invalidate CloudFront + run: aws cloudfront create-invalidation --distribution-id EBBGP8E0MLQNJ --paths "/*" diff --git a/.gitignore b/.gitignore index a547bf3..9a338ce 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,14 @@ # Logs logs +html *.log npm-debug.log* yarn-debug.log* yarn-error.log* pnpm-debug.log* lerna-debug.log* +test-results +playwright-report node_modules dist @@ -22,3 +25,13 @@ dist-ssr *.njsproj *.sln *.sw? + +# Virtual environments +venv/ +.venv/ +env/ +.env/ +*.env + +# CDK +cdk.out/ diff --git a/HLD_optimized.docx b/HLD_optimized.docx new file mode 100644 index 0000000..b9d73a2 Binary files /dev/null and b/HLD_optimized.docx differ diff --git a/HLD_optimized.pptx b/HLD_optimized.pptx new file mode 100644 index 0000000..89d2174 Binary files /dev/null and b/HLD_optimized.pptx differ diff --git a/business_overview.pptx b/business_overview.pptx new file mode 100644 index 0000000..2706ca9 Binary files /dev/null and b/business_overview.pptx differ diff --git a/cdk.context.json b/cdk.context.json new file mode 100644 index 0000000..96c6a81 --- /dev/null +++ b/cdk.context.json @@ -0,0 +1,6 @@ +{ + "hosted-zone:account=764709663363:domainName=tipro.in:region=ap-south-2": { + "Id": "/hostedzone/Z04761903HH8T88QWE7JX", + "Name": "tipro.in." + } +} diff --git a/cdk.json b/cdk.json new file mode 100644 index 0000000..578c22e --- /dev/null +++ b/cdk.json @@ -0,0 +1,68 @@ +{ + "app": "npx tsx cdk/app.ts", + "watch": { + "include": [ + "cdk/**" + ], + "exclude": [ + "README.md", + "cdk*.json", + "**/*.d.ts", + "**/*.js", + "tsconfig.json", + "package*.json", + "yarn.lock", + "node_modules", + "test" + ] + }, + "context": { + "@aws-cdk/aws-lambda:recognizeLayerVersion": true, + "@aws-cdk/core:checkSecretUsage": true, + "@aws-cdk/core:target-partitions": [ + "aws", + "aws-cn" + ], + "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, + "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, + "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true, + "@aws-cdk/aws-iam:minimizePolicies": true, + "@aws-cdk/core:validateSnapshotRemovalPolicy": true, + "@aws-cdk/aws-codepipeline:crossAccountKeyAliasReadAccess": true, + "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, + "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, + "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, + "@aws-cdk/core:enablePartitionLiterals": true, + "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true, + "@aws-cdk/aws-iam:standardizedServicePrincipals": true, + "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true, + "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true, + "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true, + "@aws-cdk/aws-route53-patters:useCertificate": true, + "@aws-cdk/customresources:installLatestAwsSdkDefault": false, + "@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true, + "@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true, + "@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true, + "@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true, + "@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachment": true, + "@aws-cdk/aws-redshift:columnId": true, + "@aws-cdk/aws-stepfunctions-tasks:invokeLambdaOutputIncludesPayload": true, + "@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true, + "@aws-cdk/core:includePrefixInUniqueNameGeneration": true, + "@aws-cdk/aws-efs:denyAnonymousAccess": true, + "@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true, + "@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion": true, + "@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": true, + "@aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters": true, + "@aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier": true, + "@aws-cdk/aws-rds:preventRenderingDeprecatedCredentials": true, + "@aws-cdk/aws-codepipeline-actions:useNewDefaultBranchForCodeCommitSource": true, + "@aws-cdk/aws-cloudwatch-actions:changeLambdaPermissionLogicalIdForLambdaAction": true, + "@aws-cdk/aws-codepipeline:crossAccountKeysDefaultValueToFalse": true, + "@aws-cdk/aws-codepipeline:defaultPipelineTypeToV2": true, + "@aws-cdk/aws-kms:reduceCrossAccountRegionPolicyScope": true, + "@aws-cdk/aws-eks:nodegroupNameAttribute": true, + "@aws-cdk/aws-ec2:ebsDefaultGp3Volume": true, + "@aws-cdk/aws-ecs:removeDefaultDeploymentAlarm": true + } +} diff --git a/cdk/app.ts b/cdk/app.ts new file mode 100644 index 0000000..a82a37b --- /dev/null +++ b/cdk/app.ts @@ -0,0 +1,21 @@ +#!/usr/bin/env node +import 'source-map-support/register.js'; +import * as cdk from 'aws-cdk-lib'; +import { BetaSupplierSiteStack } from './betasupplier-site-stack.js'; + +const app = new cdk.App(); +new BetaSupplierSiteStack(app, 'BetaSupplierSiteStack', { + /* If you don't specify 'env', this stack will be environment-agnostic. + * Account/Region-dependent features and context lookups will not work, + * but a single synthesized template can be deployed anywhere. */ + + /* Uncomment the next line to specialize this stack for the AWS Account + * and Region that are implied by the current CLI configuration. */ + env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION }, + + /* Uncomment the next line if you know exactly what Account and Region you + * want to deploy the stack to. */ + // env: { account: '123456789012', region: 'us-east-1' }, + + /* For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html */ +}); diff --git a/cdk/betasupplier-site-stack.ts b/cdk/betasupplier-site-stack.ts new file mode 100644 index 0000000..5284e30 --- /dev/null +++ b/cdk/betasupplier-site-stack.ts @@ -0,0 +1,105 @@ +import * as cdk from 'aws-cdk-lib'; +import { Construct } from 'constructs'; +import * as s3 from 'aws-cdk-lib/aws-s3'; +import * as cloudfront from 'aws-cdk-lib/aws-cloudfront'; +import * as origins from 'aws-cdk-lib/aws-cloudfront-origins'; +import * as route53 from 'aws-cdk-lib/aws-route53'; +import * as targets from 'aws-cdk-lib/aws-route53-targets'; +import * as acm from 'aws-cdk-lib/aws-certificatemanager'; + +export class BetaSupplierSiteStack extends cdk.Stack { + constructor(scope: Construct, id: string, props?: cdk.StackProps) { + super(scope, id, props); + + // 1. Create S3 Bucket for Website hosting + const websiteBucket = new s3.Bucket(this, 'BetaSupplierSiteBucket', { + bucketName: `betasuppliersite-${this.account}-${this.region}`, + removalPolicy: cdk.RemovalPolicy.DESTROY, + autoDeleteObjects: true, + blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL, // Secure by default, OAI will be used + encryption: s3.BucketEncryption.S3_MANAGED, + }); + + const ec2Origin = new origins.HttpOrigin('api.tipro.in', { + protocolPolicy: cloudfront.OriginProtocolPolicy.HTTP_ONLY, + httpPort: 8080, + }); + + // Look up the tipro.in hosted zone + const zone = route53.HostedZone.fromLookup(this, 'TiproZone', { + domainName: 'tipro.in', + }); + + // Create Route53 A Record for the EC2 API server + // This allows us to use an IP address indirectly, by mapping api.tipro.in to it. + new route53.ARecord(this, 'ApiRecord', { + recordName: 'api.tipro.in', + target: route53.RecordTarget.fromIpAddresses('16.113.57.127'), + zone + }); + + // Create a certificate in us-east-1 for CloudFront + const certificate = new acm.DnsValidatedCertificate(this, 'SiteCertificate', { + domainName: 'partners.tipro.in', + hostedZone: zone, + region: 'us-east-1', // CloudFront requires certificates to be in us-east-1 + }); + + // 2. Create CloudFront Distribution + // origins.S3BucketOrigin.withOriginAccessControl will automatically create an Origin Access Control (OAC) + // and update the S3 bucket policy to allow access only from this CloudFront distribution. + const distribution = new cloudfront.Distribution(this, 'BetaSupplierSiteDistribution', { + domainNames: ['partners.tipro.in'], + certificate: certificate, + defaultRootObject: 'index.html', + defaultBehavior: { + origin: origins.S3BucketOrigin.withOriginAccessControl(websiteBucket), + viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS, + allowedMethods: cloudfront.AllowedMethods.ALLOW_GET_HEAD, + cachedMethods: cloudfront.CachedMethods.CACHE_GET_HEAD, + cachePolicy: cloudfront.CachePolicy.CACHING_OPTIMIZED, + }, + additionalBehaviors: { + '/api/*': { + origin: ec2Origin, + viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS, + allowedMethods: cloudfront.AllowedMethods.ALLOW_ALL, + cachePolicy: cloudfront.CachePolicy.CACHING_DISABLED, + originRequestPolicy: cloudfront.OriginRequestPolicy.ALL_VIEWER, + }, + }, + errorResponses: [ + { + httpStatus: 404, + responseHttpStatus: 200, // Return 200 for SPAs + responsePagePath: '/index.html', + ttl: cdk.Duration.seconds(0), + }, + { + httpStatus: 403, + responseHttpStatus: 200, // Return 200 for SPAs + responsePagePath: '/index.html', + ttl: cdk.Duration.seconds(0), + } + ] + }); + + // Outputs + new cdk.CfnOutput(this, 'BetaSupplierSiteBucketName', { + value: websiteBucket.bucketName, + description: 'The name of the S3 bucket for the betasupplier site', + }); + + new cdk.CfnOutput(this, 'BetaSupplierSiteCloudFrontDomainName', { + value: distribution.distributionDomainName, + description: 'The domain name of the CloudFront distribution for betasupplier site', + }); + + // 3. Create Route53 A Record to point to the CloudFront Distribution + new route53.ARecord(this, 'SiteAliasRecord', { + recordName: 'partners.tipro.in', + target: route53.RecordTarget.fromAlias(new targets.CloudFrontTarget(distribution)), + zone + }); + } +} diff --git a/cdk/betasupplier-site-stack.ts.orig b/cdk/betasupplier-site-stack.ts.orig new file mode 100644 index 0000000..2be537b --- /dev/null +++ b/cdk/betasupplier-site-stack.ts.orig @@ -0,0 +1,59 @@ +import * as cdk from 'aws-cdk-lib'; +import { Construct } from 'constructs'; +import * as s3 from 'aws-cdk-lib/aws-s3'; +import * as cloudfront from 'aws-cdk-lib/aws-cloudfront'; +import * as origins from 'aws-cdk-lib/aws-cloudfront-origins'; + +export class BetaSupplierSiteStack extends cdk.Stack { + constructor(scope: Construct, id: string, props?: cdk.StackProps) { + super(scope, id, props); + + // 1. Create S3 Bucket for Website hosting + const websiteBucket = new s3.Bucket(this, 'BetaSupplierSiteBucket', { + bucketName: `betasuppliersite-${this.account}-${this.region}`, + removalPolicy: cdk.RemovalPolicy.DESTROY, + autoDeleteObjects: true, + blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL, // Secure by default, OAI will be used + encryption: s3.BucketEncryption.S3_MANAGED, + }); + + // 2. Create CloudFront Distribution + // origins.S3Origin will automatically create an Origin Access Identity (OAI) + // and update the S3 bucket policy to allow access only from this CloudFront distribution. + const distribution = new cloudfront.Distribution(this, 'BetaSupplierSiteDistribution', { + defaultRootObject: 'index.html', + defaultBehavior: { + origin: new origins.S3Origin(websiteBucket), + viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS, + allowedMethods: cloudfront.AllowedMethods.ALLOW_GET_HEAD, + cachedMethods: cloudfront.CachedMethods.CACHE_GET_HEAD, + cachePolicy: cloudfront.CachePolicy.CACHING_OPTIMIZED, + }, + errorResponses: [ + { + httpStatus: 404, + responseHttpStatus: 200, // Return 200 for SPAs + responsePagePath: '/index.html', + ttl: cdk.Duration.seconds(0), + }, + { + httpStatus: 403, + responseHttpStatus: 200, // Return 200 for SPAs + responsePagePath: '/index.html', + ttl: cdk.Duration.seconds(0), + } + ] + }); + + // Outputs + new cdk.CfnOutput(this, 'BetaSupplierSiteBucketName', { + value: websiteBucket.bucketName, + description: 'The name of the S3 bucket for the betasupplier site', + }); + + new cdk.CfnOutput(this, 'BetaSupplierSiteCloudFrontDomainName', { + value: distribution.distributionDomainName, + description: 'The domain name of the CloudFront distribution for betasupplier site', + }); + } +} diff --git a/cypress/e2e/api.cy.ts b/cypress/e2e/api.cy.ts index 342b6bd..9a699c7 100644 --- a/cypress/e2e/api.cy.ts +++ b/cypress/e2e/api.cy.ts @@ -1,78 +1,60 @@ describe('Seller Central Django Backend API E2E Tests', () => { - const backendUrl = 'http://localhost:8000/api'; + const backendUrl = 'http://16.113.57.127:8080/api'; it('verifies product list', () => { - cy.request({ - method: 'GET', - url: `${backendUrl}/products/` - }).then((response) => { + cy.request(`${backendUrl}/products/`).then((response) => { expect(response.status).to.eq(200); expect(response.body).to.be.an('array'); }); }); it('creates and deletes a product', () => { - const testSku = `CY-SKU-${Date.now()}`; - cy.request({ - method: 'POST', - url: `${backendUrl}/products/`, - body: { - title: 'Cypress Test Pot', - category: 'Home Decor', - price: '45.00', - stock: 5, - sku: testSku - } + const uniqueSku = `SKU-CY-${Date.now()}`; + cy.request('POST', `${backendUrl}/products/`, { + title: 'Cypress Test Silk Scarf', + category: 'Apparel', + price: '55.00', + stock: 20, + sku: uniqueSku }).then((response) => { expect(response.status).to.eq(201); - expect(response.body.sku).to.eq(testSku); - const productId = response.body.id; + expect(response.body.sku).to.eq(uniqueSku); + const prodId = response.body.id; - // Delete the product - cy.request({ - method: 'DELETE', - url: `${backendUrl}/products/${productId}/` - }).then((delResponse) => { - expect(delResponse.status).to.eq(204); + // Delete the created product + cy.request('DELETE', `${backendUrl}/products/${prodId}/`).then((delRes) => { + expect(delRes.status).to.eq(204); // django rest framework delete returns 204 No Content }); }); }); it('verifies order list and acceptance', () => { - cy.request({ - method: 'GET', - url: `${backendUrl}/orders/` - }).then((response) => { + cy.request(`${backendUrl}/orders/`).then((response) => { expect(response.status).to.eq(200); expect(response.body).to.be.an('array'); - const order = response.body.find((o: any) => o.status === 'Pending Acceptance'); - if (order) { - cy.request({ - method: 'POST', - url: `${backendUrl}/orders/${order.id}/accept/` - }).then((acceptResponse) => { - expect(acceptResponse.status).to.eq(200); - expect(acceptResponse.body.status).to.eq('Ready to Ship'); + expect(response.body.length).to.be.greaterThan(0); + + const pendingOrder = response.body.find((o: any) => o.status === 'Pending Acceptance'); + if (pendingOrder) { + cy.request('POST', `${backendUrl}/orders/${pendingOrder.id}/accept/`).then((acceptRes) => { + expect(acceptRes.status).to.eq(200); + expect(acceptRes.body.status).to.eq('Ready to Ship'); }); } }); }); it('verifies wallet summary and payout withdrawal', () => { - cy.request({ - method: 'GET', - url: `${backendUrl}/wallet/` - }).then((response) => { + cy.request(`${backendUrl}/wallet/`).then((response) => { expect(response.status).to.eq(200); - const initialOutstanding = parseFloat(response.body.outstanding); - if (initialOutstanding > 10) { - cy.request({ - method: 'POST', - url: `${backendUrl}/wallet/withdraw/`, - body: { amount: '10.00' } - }).then((withdrawResponse) => { - expect(withdrawResponse.status).to.eq(200); - expect(parseFloat(withdrawResponse.body.outstanding)).to.eq(initialOutstanding - 10); + expect(response.body).to.have.property('outstanding'); + expect(response.body).to.have.property('withdrawn'); + + const currentOutstanding = parseFloat(response.body.outstanding); + if (currentOutstanding > 10) { + cy.request('POST', `${backendUrl}/wallet/withdraw/`, { amount: '10.00' }).then((withdrawRes) => { + expect(withdrawRes.status).to.eq(200); + expect(parseFloat(withdrawRes.body.outstanding)).to.eq(currentOutstanding - 10); }); } }); diff --git a/cypress/e2e/onboarding.cy.ts b/cypress/e2e/onboarding.cy.ts index 368ccf9..a520ec2 100644 --- a/cypress/e2e/onboarding.cy.ts +++ b/cypress/e2e/onboarding.cy.ts @@ -6,9 +6,12 @@ describe('Supplier Portal Onboarding & Feature E2E Flow', () => { // 2. Registration Page cy.contains('Get Started Today').click(); - cy.get('input#reg-email').type('test_seller@example.com'); + const randUser = `test_seller_${Date.now()}@example.com`; + cy.get('input#reg-email').type(randUser); cy.get('input#reg-phone').type('9876543210'); cy.get('input#reg-pass').type('super_secure_pass_123'); + cy.get('input#reg-confirm-pass').type('super_secure_pass_123'); + cy.get('input#reg-policy').check(); cy.get('button').contains('Register Business').click(); // 3. Step 1: Contact Verification @@ -27,8 +30,8 @@ describe('Supplier Portal Onboarding & Feature E2E Flow', () => { // 4. Step 2: Tax & Identity Verification cy.contains('Step 2 of 3: Tax & Identity Verification').should('be.visible'); cy.get('input#verify-gst').clear().type('29AAAAA1111A1Z1'); - cy.contains('Verify GSTIN').click(); - cy.contains('GSTIN successfully verified with government registry.').should('be.visible'); + cy.contains('Submit GSTIN').click(); + cy.contains('verification will be completed within next 24 hrs').should('be.visible'); // Upload mock Aadhaar & PAN files cy.get('input#aadhar-upload').selectFile({ @@ -63,8 +66,8 @@ describe('Supplier Portal Onboarding & Feature E2E Flow', () => { cy.contains('Submit Supplier Profile').click(); // 6. Setup Welcome Tour Stepper - cy.contains('Welcome to Global Artisans Hub! 🎉').should('be.visible'); - cy.contains('Start Tour').click(); + cy.contains('Supplier Account Under Review ⏳').should('be.visible'); + cy.contains('Start Guided Tour 🎬').click(); cy.contains('📦 Products & Inventory Management').should('be.visible'); cy.contains('Next: Order Management').click(); cy.contains('🚚 Order Acceptance Control').should('be.visible'); @@ -72,7 +75,7 @@ describe('Supplier Portal Onboarding & Feature E2E Flow', () => { cy.contains('🏷️ Barcode Identification & Tracking').should('be.visible'); cy.contains('Next: Earnings & Wallet').click(); cy.contains('💼 Wallet & Payout Withdrawals').should('be.visible'); - cy.contains('Launch Dashboard 🚀').click(); + cy.contains('Explore Dashboard 🚀').click(); // 7. Check Active Dashboard Tab cy.contains('Performance Analytics').should('be.visible'); @@ -98,4 +101,54 @@ describe('Supplier Portal Onboarding & Feature E2E Flow', () => { cy.contains('Print Label').should('be.visible'); cy.contains('Download SVG').should('be.visible'); }); + + it('enforces security constraints and validation rules (negative test cases)', () => { + // 1. Visit Home and verify dashboard components are not in DOM (route guarding) + cy.visit('/'); + cy.get('.dashboard-container').should('not.exist'); + cy.get('.sidebar-menu').should('not.exist'); + + // 2. Go to Registration + cy.contains('Get Started Today').click(); + const randSecurityUser = `security_test_${Date.now()}@example.com`; + cy.get('input#reg-email').type(randSecurityUser); + cy.get('input#reg-phone').type('9000000000'); + cy.get('input#reg-pass').type('password123'); + cy.get('input#reg-confirm-pass').type('password123'); + cy.get('input#reg-policy').check(); + cy.get('button').contains('Register Business').click(); + + // 3. Step 1: Negative OTP validations + cy.contains('Step 1 of 3: Contact Verification').should('be.visible'); + + // Proceed button must be disabled initially + cy.get('button').contains('Next Step: Identity Verification').should('be.disabled'); + + // Submit invalid WhatsApp OTP + cy.contains('Send WhatsApp OTP').click(); + cy.get('input[placeholder="Enter 123456"]').first().type('000000'); + + // Capture alert for incorrect OTP + const alertStub = cy.stub(); + cy.on('window:alert', alertStub); + + cy.contains('Verify Code').first().click().then(() => { + expect(alertStub).to.have.been.calledWith('Incorrect code. Enter 123456'); + }); + cy.contains('✓ Mobile & WhatsApp Verified').should('not.exist'); + + // Correct the code to enable proceed + cy.get('input[placeholder="Enter 123456"]').first().clear().type('123456'); + cy.contains('Verify Code').first().click(); + cy.contains('✓ Mobile & WhatsApp Verified').should('be.visible'); + + // Submit invalid Email OTP + cy.contains('Send Email OTP').click(); + cy.get('input[placeholder="Enter 123456"]').last().type('999999'); + cy.contains('Verify Code').last().click().then(() => { + expect(alertStub).to.have.been.calledWith('Incorrect code. Enter 123456'); + }); + cy.contains('✓ Email Verified').should('not.exist'); + cy.get('button').contains('Next Step: Identity Verification').should('be.disabled'); + }); }); diff --git a/fix.py b/fix.py new file mode 100644 index 0000000..1f80a3b --- /dev/null +++ b/fix.py @@ -0,0 +1,5 @@ +with open('/home/vignesh/github/Tiproemail/aws_cdk/lib/betasupplier-site-stack.ts', 'r') as f: + content = f.read() +content = content.replace(' }\n errorResponses: [', ' },\n errorResponses: [') +with open('/home/vignesh/github/Tiproemail/aws_cdk/lib/betasupplier-site-stack.ts', 'w') as f: + f.write(content) diff --git a/index.html b/index.html index 4809195..9066c61 100644 --- a/index.html +++ b/index.html @@ -1,11 +1,16 @@ - +