Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Conversation

zjy2414
Copy link

@zjy2414 zjy2414 commented Aug 24, 2025

English | 简体中文

PR

PR Checklist

Please check if your PR fulfills the following requirements:

  • The commit message follows our Commit Message Guidelines
  • Tests for the changes have been added (for bug fixes / features)
  • Docs have been added / updated (for bug fixes / features)
  • Built its own designer, fully self-validated

PR Type

What kind of change does this PR introduce?

  • Bugfix
  • Feature
  • Code style update (formatting, local variables)
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • CI related changes
  • Documentation content changes
  • Test coverage improvements (mocks + cases for data binding and lifecycle hooks)

Background and solution

The generator lacked targeted tests and mock data for:

  • Data binding (including two-way binding) behaviors.
  • Lifecycle hooks execution and side effects.

This gap made it hard to detect regressions when adjusting the generator logic around bindings or hook handling.

What is the current behavior?

  • Insufficient test coverage around:
    • Binding resolution (especially two-way data binding).
    • Lifecycle hook execution order and outcomes.

What is the new behavior?

  • Comprehensive tests for:
    • Data binding (one-way and two-way), ensuring bindings are generated and updated correctly.
    • Lifecycle hooks, validating correct hook invocation and expected results.
  • New mock data to support the above scenarios.

Does this PR introduce a breaking change?

  • Yes
  • No

Summary by CodeRabbit

  • New Features

    • Added a Lifecycle page with auto-refreshing metrics and manual refresh.
    • Introduced element selection handling on the Canvas Row/Col page.
  • Improvements

    • Create VM form now uses consistent two-way bindings with centralized state; refined placeholders and label widths for better usability.
  • Routing

    • New /lifecycle route added to the app navigation.
  • Dependency Updates

    • Upgraded UI components to @opentiny/vue 3.24.0 (and icons).
    • Added TinyCard (Card) component support.

Copy link
Contributor

coderabbitai bot commented Aug 24, 2025

Walkthrough

Adds comprehensive test suites and mock schemas for data binding and lifecycle generation in the Vue generator. Updates expected app artifacts: new LifeCyclePage, router entry, component/version bumps, and refactors to views to use nested reactive state with v-model. Extends generator mock data with new pages, state, methods, and lifecycle definitions.

Changes

Cohort / File(s) Summary
Data Binding Tests
packages/vue-generator/test/testcases/data-binding/basic.test.js, .../advanced.test.js, .../edge-case.test.js, .../form.test.js
New Vitest suites validating basic, advanced, edge, and complex-form data-binding generation, nested reactive state, component presence, and v-model counts; writes generated .vue files to result directories.
Data Binding Mock Schemas
packages/vue-generator/test/testcases/data-binding/mockData.js
Adds four exported schemas: basic, advanced, edge-case, and complex-form with componentsMap, pageSchema, JSExpression bindings to state.*, and initial reactive state.
Lifecycle Tests
packages/vue-generator/test/testcases/lifecycle/lifecycle.test.js
New tests asserting lifecycle hook generation (mounted/unmounted/updated/beforeMount, errors captured), absence of prop-based lifecycle bindings, and content checks for generated pages.
Lifecycle Mock Schemas
packages/vue-generator/test/testcases/lifecycle/mockData.js
Adds three exported schemas: basic, complex, and mixed lifecycle cases, including state, methods, and lifeCycles using JSFunction/JSExpression.
Generator Expected App: New Page & Router
packages/vue-generator/test/testcases/generator/expected/appdemo01/src/views/LifeCyclePage.vue, .../src/router/index.js
Introduces LifeCyclePage.vue with reactive metrics, interval-driven auto-refresh, visibility handling, and full lifecycle hooks; registers new route lifecycle.
Generator Expected App: View Refactors
.../src/views/createVm.vue, .../src/views/testCanvasRowCol.vue
Migrates to nested reactive state with v-model across many controls; adds TinyButton import. Updates canvas page state shape and adds handleElementClick method exposed via wrap.
Generator Mock Data & Component Map
packages/vue-generator/test/testcases/generator/mockData.js
Expands app schema: adds LifeCyclePage definition, augments page states (formData/inputValues/selectValues), introduces canvas state/method, and updates componentsMap (version bumps, adds TinyCard/Card, PortalPermissionBlock, TinyButton).
Dependency Versions
.../generator/expected/appdemo01/package.json
Bumps @opentiny/vue and @opentiny/vue-icon to 3.24.0.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant Page as LifeCyclePage.vue
  participant Vue as Vue Lifecycle
  participant Doc as Document Visibility
  participant Timer as setInterval(30s)

  Vue->>Page: onBeforeMount()
  Vue->>Page: onMounted()
  Page->>Page: refreshData()
  Page->>Page: startAutoRefresh()
  Page->>Timer: start interval
  Doc-->>Page: visibilitychange (hidden)
  Page->>Page: stopAutoRefresh()
  Page->>Timer: clear interval
  Doc-->>Page: visibilitychange (visible)
  Page->>Page: startAutoRefresh()
  Page->>Timer: restart interval
  User->>Page: Click "Refresh Data"
  Page->>Page: refreshData()
  Vue->>Page: onUpdated() [state changed]
  Vue->>Page: onDeactivated()/onActivated() [keep-alive]
  Vue->>Page: onUnmounted() / onBeforeUnmount()
  Page->>Timer: clear interval
Loading
sequenceDiagram
  autonumber
  participant Tests as Vitest Suites
  participant Gen as generateApp()
  participant Schemas as Mock Schemas
  participant Files as Generated .vue

  Tests->>Schemas: load schema (basic/advanced/edge/complex/lifecycle)
  Tests->>Gen: generateApp(schema)
  Gen-->>Files: emit .vue files
  Tests->>Files: read *.vue
  Tests-->>Tests: assert v-models, state structure, hooks, imports
  Tests->>Files: write to result/* (debug)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

I thump my paws, the builds take flight,
New lifecycles dance in morning light.
v-model vines entwine the form,
State nests cozy, safe and warm.
Routes hop in, components bloom—
Ship it quick, before the moon! 🌕🐇

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions github-actions bot added ospp ospp test test of ut/e2e... labels Aug 24, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

🧹 Nitpick comments (28)
packages/vue-generator/test/testcases/generator/expected/appdemo01/src/views/testCanvasRowCol.vue (1)

49-54: Event handler correctly mutates nested reactive state; optional ergonomic tweak.

Current pattern with wrap(function handleElementClick...) and this.state... matches existing generator conventions—keep it. Optionally, if we later favor closure style over this, we could switch to an arrow fn using the captured state.

-const handleElementClick = wrap(function handleElementClick(elementId) {
-  this.state.uiState.selectedElement = elementId
-})
+const handleElementClick = wrap(function handleElementClick(elementId) {
+  // keep "this" style to align with current wrap semantics
+  this.state.uiState.selectedElement = elementId
+})
packages/vue-generator/test/testcases/generator/expected/appdemo01/src/views/createVm.vue (1)

58-58: Solid v-model migration to centralized nested state; add keys for v-for and minor UX polish.

  • The two-way bindings mapped into state.formData, state.inputValues, and state.selectValues look consistent and testable.
  • In the “数据盘” repeater, add a stable :key to avoid Vue warnings and improve patch efficiency. You can also iterate with (disk, idx) for clarity.
-          <div v-for="() in state.dataDisk" style="margin-top: 12px; display: flex">
+          <div v-for="(disk, idx) in state.dataDisk" :key="idx" style="margin-top: 12px; display: flex">
  • Import aliases ButtonGroup as TinyButtonGroup, Button as TinyButton align with the 3.24.0 API and the rest of this PR’s expected outputs—good.

Also applies to: 92-92, 105-105, 116-116, 124-126, 217-217, 226-226, 267-267, 273-277, 299-299, 305-309, 312-312, 351-355, 399-401, 416-433

packages/vue-generator/test/testcases/lifecycle/mockData.js (2)

16-18: Unify component version declarations with fixtures (optional).

Schemas still declare '^3.10.0' while the expected fixture pins 3.24.0. Aligning these to '^3.24.0' keeps version intent coherent across tests and reduces cognitive overhead.

-      version: '^3.10.0',
+      version: '^3.24.0',

Apply to all componentsMap entries in this file.

Also applies to: 89-91, 177-186


166-260: Great coverage: mixed lifecycle + data-binding schema. Add a test to exercise it.

mixedLifecycleSchema is a useful scenario (child prop-level lifecycle + binding). It isn’t currently exercised by tests; consider adding/adjusting a spec to validate its transformation (no prop-bound onMounted=, presence of script-level vue.onMounted, and emitted method calls like focusInput()).

I can provide a test diff (see lifecycle.test.js comment) to wire this in.

packages/vue-generator/test/testcases/generator/expected/appdemo01/src/views/LifeCyclePage.vue (2)

89-93: Fix onUpdated log to reference existing state

this.state.loginCount doesn’t exist; logs will show undefined. Prefer logging relevant metrics.

Apply this diff:

-vue.onUpdated(
-  wrap(function onUpdated() {
-    console.log('页面已更新', this.state.loginCount)
-  })
-)
+vue.onUpdated(
+  wrap(function onUpdated() {
+    console.log('页面已更新', {
+      activeUsers: this.state.activeUsers,
+      systemLoad: this.state.systemLoad
+    })
+  })
+)

18-21: Drop macro imports and unused destructure for leaner SFC

  • In <script setup>, defineProps/defineEmits are compile-time macros; importing them is unnecessary and can confuse tooling.
  • t is destructured but not used.

Apply this diff:

 import { Button as TinyButton, Card as TinyCard } from '@opentiny/vue'
 import * as vue from 'vue'
-import { defineProps, defineEmits } from 'vue'
 import { I18nInjectionKey } from 'vue-i18n'
@@
-const { t, lowcodeWrap, stores } = vue.inject(I18nInjectionKey).lowcode()
+const { lowcodeWrap, stores } = vue.inject(I18nInjectionKey).lowcode()

Also applies to: 26-26

packages/vue-generator/test/testcases/generator/mockData.js (3)

1087-1091: Consistent conversion to JSExpression + model for v-model sources

All updated bindings now point to the new nested paths (state.formData/, state.inputValues/, state.selectValues/*). This should produce correct v-model generation.

Small follow-up:

  • Ensure default values match component expectations (numbers vs strings) to avoid implicit coercion when two-way binding. If Tiny components expect string values, the current string defaults are fine.

Also applies to: 1140-1144, 1182-1186, 1220-1224, 1259-1264, 1470-1473, 1492-1496, 1576-1579, 1600-1603, 1661-1664, 1685-1688, 1706-1709, 1822-1825


1252-1252: UI nit: width increased to 120px

The label width bump is fine; consider checking adjacent labels for alignment to avoid misaligned baselines in tight layouts.


2539-2545: Verify component map versions for @opentiny/vue

Most entries are pinned to 3.24.0, while TinyCard uses ^3.10.0. That resolves to 3.24.0 and won’t conflict, but aligning versions can prevent duplicate installs in other contexts.

If you want to harmonize:

-      version: '^3.10.0',
+      version: '3.24.0',

Also applies to: 2611-2617

packages/vue-generator/test/testcases/data-binding/edge-case.test.js (2)

71-79: Gate debug artifact writes behind an env switch

Avoid polluting the repo during CI and local runs by making writes opt-in.

Apply this diff:

-    const outputDir = path.resolve(__dirname, './result/edge-case')
-    fs.mkdirSync(outputDir, { recursive: true })
-
-    for (const { fileName, fileContent } of genResult) {
-      if (fileName.endsWith('.vue')) {
-        fs.writeFileSync(path.join(outputDir, fileName), fileContent)
-      }
-    }
+    if (process.env.WRITE_GEN_FIXTURES === '1') {
+      const outputDir = path.resolve(__dirname, './result/edge-case')
+      fs.mkdirSync(outputDir, { recursive: true })
+      for (const { fileName, fileContent } of genResult) {
+        if (fileName.endsWith('.vue')) {
+          fs.writeFileSync(path.join(outputDir, fileName), fileContent)
+        }
+      }
+    }

1-6: Make __dirname robust for ESM runners

Vitest often runs tests as ESM; in that case __dirname may be undefined. Provide a fallback via import.meta.url.

If your repo uses "type": "module", apply:

 import { expect, test, describe } from 'vitest'
 import path from 'path'
 import fs from 'fs'
+import { fileURLToPath } from 'url'
@@
-    const outputDir = path.resolve(__dirname, './result/edge-case')
+    const __filename = typeof __filename !== 'undefined' ? __filename : fileURLToPath(import.meta.url)
+    const __dirnameSafe = typeof __dirname !== 'undefined' ? __dirname : path.dirname(__filename)
+    const outputDir = path.resolve(__dirnameSafe, './result/edge-case')

Alternatively, confirm you’re running in CJS; if so, you can skip this change.

Also applies to: 71-73

packages/vue-generator/test/testcases/data-binding/basic.test.js (2)

60-69: Tighten the v-model regex to avoid matching modifiers/arguments

Current /v-model[^=]*="..."/ may match v-model:foo or v-model.trim. If you intend only default bindings, restrict it.

Apply this diff:

-    const vModelMatches = content.match(/v-model[^=]*="[^"]+"/g) || []
+    const vModelMatches = content.match(/(?<!:)v-model="[^"]+"/g) || []

40-49: Gate debug artifact writes behind an env switch (consistency with other tests)

Prevents stray files in CI/local runs; keeps behavior consistent with edge-case test suggestion.

Apply this diff:

-    const outputDir = path.resolve(__dirname, './result/basic')
-    fs.mkdirSync(outputDir, { recursive: true })
-
-    for (const { fileName, fileContent } of genResult) {
-      if (fileName.endsWith('.vue')) {
-        fs.writeFileSync(path.join(outputDir, fileName), fileContent)
-      }
-    }
+    if (process.env.WRITE_GEN_FIXTURES === '1') {
+      const outputDir = path.resolve(__dirname, './result/basic')
+      fs.mkdirSync(outputDir, { recursive: true })
+      for (const { fileName, fileContent } of genResult) {
+        if (fileName.endsWith('.vue')) {
+          fs.writeFileSync(path.join(outputDir, fileName), fileContent)
+        }
+      }
+    }
packages/vue-generator/test/testcases/data-binding/mockData.js (3)

282-286: Avoid empty JSExpression for modelValue in edge-case

An empty JSExpression can lead to invalid v-model like v-model="" if the transformer doesn’t guard for it. Make the intent explicit: static empty value without data-binding.

-            modelValue: {
-              type: 'JSExpression',
-              value: '' // 空字段
-            }
+            // 静态空值,无数据绑定
+            modelValue: ''

Alternatively, if you want to keep JSExpression form while ensuring it won't become v-model:

-            modelValue: {
-              type: 'JSExpression',
-              value: ''
-            }
+            modelValue: {
+              type: 'JSExpression',
+              value: "''",
+              model: false
+            }

361-372: Confirm tag expectation: schema uses

, tests assert

The schema’s root is componentName: 'form', yet form.test.js asserts <div class="complex-form">. Unless the generator normalizes form to div, this will fail.

Prefer adjusting the test to accept either <form> or <div> with the same class. See suggested test change in form.test.js comment.

If you intended a <div>, switch componentName to 'div' here to match the test.


11-26: Deduplicate componentsMap declarations

The component entries (package, version, destructuring) are repeated across schemas. Centralizing them reduces drift when versions change.

Consider exporting shared factories, e.g.,

  • makeTinyInput(), makeTinySelect(), makeTinyCheckbox(), makeTinySwitch(), makeTinyRadio()
  • or a const base = { package: '@opentiny/vue', version: '^3.10.0', destructuring: true }

Then spread and override componentName/exportName per schema.

Also applies to: 97-126, 336-358

packages/vue-generator/test/testcases/data-binding/advanced.test.js (4)

52-54: Harden file lookup and guard against undefined

Use endsWith to avoid path prefix mismatches and assert existence before dereferencing.

-    const vueFile = genResult.find((file) => file.fileName === 'AdvancedFormPage.vue')
-    const content = vueFile.fileContent
+    const vueFile = genResult.find((file) => file.fileName?.endsWith('AdvancedFormPage.vue'))
+    expect(vueFile).toBeDefined()
+    const content = vueFile.fileContent

74-76: Repeat the guard for subsequent tests

Avoid potential Cannot read properties of undefined if generation shape changes.

-    const vueFile = genResult.find((file) => file.fileName === 'AdvancedFormPage.vue')
-    const content = vueFile.fileContent
+    const vueFile = genResult.find((file) => file.fileName?.endsWith('AdvancedFormPage.vue'))
+    expect(vueFile).toBeDefined()
+    const content = vueFile.fileContent

92-94: Repeat the guard for the radio group test

Same robustness improvement as above.

-    const vueFile = genResult.find((file) => file.fileName === 'AdvancedFormPage.vue')
-    const content = vueFile.fileContent
+    const vueFile = genResult.find((file) => file.fileName?.endsWith('AdvancedFormPage.vue'))
+    expect(vueFile).toBeDefined()
+    const content = vueFile.fileContent

47-67: Optional: generate once per suite to speed up tests

You regenerate for each test. Caching genResult in beforeAll reduces runtime and IO. Keep a test that ensures idempotency if needed.

I can provide a consolidated pattern if you’d like.

packages/vue-generator/test/testcases/data-binding/form.test.js (8)

50-52: Harden file lookup and guard against undefined (test 2)

Consistent with advanced tests: avoid brittle equality and assert existence.

-    const vueFile = genResult.find((file) => file.fileName === 'ComplexFormPage.vue')
+    const vueFile = genResult.find((file) => file.fileName?.endsWith('ComplexFormPage.vue'))
+    expect(vueFile).toBeDefined()
     const content = vueFile.fileContent

68-70: Harden file lookup and guard against undefined (test 3)

-    const vueFile = genResult.find((file) => file.fileName === 'ComplexFormPage.vue')
+    const vueFile = genResult.find((file) => file.fileName?.endsWith('ComplexFormPage.vue'))
+    expect(vueFile).toBeDefined()
     const content = vueFile.fileContent

85-87: Harden file lookup and guard against undefined (test 4)

-    const vueFile = genResult.find((file) => file.fileName === 'ComplexFormPage.vue')
+    const vueFile = genResult.find((file) => file.fileName?.endsWith('ComplexFormPage.vue'))
+    expect(vueFile).toBeDefined()
     const content = vueFile.fileContent

102-104: Harden file lookup and guard against undefined (test 5)

-    const vueFile = genResult.find((file) => file.fileName === 'ComplexFormPage.vue')
+    const vueFile = genResult.find((file) => file.fileName?.endsWith('ComplexFormPage.vue'))
+    expect(vueFile).toBeDefined()
     const content = vueFile.fileContent

129-131: Harden file lookup and guard against undefined (test 6)

-    const vueFile = genResult.find((file) => file.fileName === 'ComplexFormPage.vue')
+    const vueFile = genResult.find((file) => file.fileName?.endsWith('ComplexFormPage.vue'))
+    expect(vueFile).toBeDefined()
     const content = vueFile.fileContent

133-135: Relax import assertion to tolerate formatting

Use a regex for spacing/line-break variations in the import list.

-    expect(content).toContain(
-      "import { Input as TinyInput, Select as TinySelect, Checkbox as TinyCheckbox } from '@opentiny/vue'"
-    )
+    const importRegex =
+      /import\s*{\s*Input\s+as\s+TinyInput\s*,\s*Select\s+as\s+TinySelect\s*,\s*Checkbox\s+as\s+TinyCheckbox\s*}\s*from\s*['"]@opentiny\/vue['"]/
+    expect(content).toMatch(importRegex)

113-121: Optional: gate artifact export behind an env flag

Writing files during tests can slow CI and create noisy artifacts locally. Make it opt-in.

-    // 写入测试结果
-    const outputDir = path.resolve(__dirname, './result/complex-form')
-    fs.mkdirSync(outputDir, { recursive: true })
-
-    for (const { fileName, fileContent } of genResult) {
-      if (fileName.endsWith('.vue')) {
-        fs.writeFileSync(path.join(outputDir, fileName), fileContent)
-      }
-    }
+    // 写入测试结果(可选:设置 EXPORT_GEN=1 启用)
+    if (process.env.EXPORT_GEN) {
+      const outputDir = path.resolve(__dirname, './result/complex-form')
+      fs.mkdirSync(outputDir, { recursive: true })
+      for (const { fileName, fileContent } of genResult) {
+        if (fileName.endsWith('.vue')) {
+          fs.writeFileSync(path.join(outputDir, fileName), fileContent)
+        }
+      }
+    }

7-12: Generate once per suite to reduce overhead

You can move generateApp() + generate(schema) to beforeAll and share genResult across tests; current repeated generation is unnecessary here.

I can provide a refactor sketch if you want to proceed.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 108919d and c671dd9.

📒 Files selected for processing (13)
  • packages/vue-generator/test/testcases/data-binding/advanced.test.js (1 hunks)
  • packages/vue-generator/test/testcases/data-binding/basic.test.js (1 hunks)
  • packages/vue-generator/test/testcases/data-binding/edge-case.test.js (1 hunks)
  • packages/vue-generator/test/testcases/data-binding/form.test.js (1 hunks)
  • packages/vue-generator/test/testcases/data-binding/mockData.js (1 hunks)
  • packages/vue-generator/test/testcases/generator/expected/appdemo01/package.json (1 hunks)
  • packages/vue-generator/test/testcases/generator/expected/appdemo01/src/router/index.js (1 hunks)
  • packages/vue-generator/test/testcases/generator/expected/appdemo01/src/views/LifeCyclePage.vue (1 hunks)
  • packages/vue-generator/test/testcases/generator/expected/appdemo01/src/views/createVm.vue (10 hunks)
  • packages/vue-generator/test/testcases/generator/expected/appdemo01/src/views/testCanvasRowCol.vue (1 hunks)
  • packages/vue-generator/test/testcases/generator/mockData.js (23 hunks)
  • packages/vue-generator/test/testcases/lifecycle/lifecycle.test.js (1 hunks)
  • packages/vue-generator/test/testcases/lifecycle/mockData.js (1 hunks)
🧰 Additional context used
🧠 Learnings (6)
📚 Learning: 2024-09-30T07:51:10.036Z
Learnt from: chilingling
PR: opentiny/tiny-engine#837
File: packages/vue-generator/src/plugins/genDependenciesPlugin.js:66-66
Timestamp: 2024-09-30T07:51:10.036Z
Learning: In the `tiny-engine` project, `opentiny/tiny-engine-dsl-vue` refers to the current package itself, and importing types from it may cause circular dependencies.

Applied to files:

  • packages/vue-generator/test/testcases/generator/expected/appdemo01/package.json
  • packages/vue-generator/test/testcases/generator/mockData.js
📚 Learning: 2025-01-14T06:49:00.797Z
Learnt from: gene9831
PR: opentiny/tiny-engine#1011
File: packages/configurator/src/router-select-configurator/RouterSelectConfigurator.vue:63-73
Timestamp: 2025-01-14T06:49:00.797Z
Learning: In the tiny-engine project, the SvgIcon component is globally registered and available throughout Vue components without requiring explicit imports.

Applied to files:

  • packages/vue-generator/test/testcases/generator/expected/appdemo01/package.json
  • packages/vue-generator/test/testcases/generator/expected/appdemo01/src/views/createVm.vue
📚 Learning: 2025-01-14T11:09:32.013Z
Learnt from: rhlin
PR: opentiny/tiny-engine#1011
File: packages/vue-generator/src/plugins/genRouterPlugin.js:122-125
Timestamp: 2025-01-14T11:09:32.013Z
Learning: The router configuration in Vue Generator should maintain the current structure without a catch-all route for 404 handling or modification to the root path prefix, as these are not required at this time.

Applied to files:

  • packages/vue-generator/test/testcases/generator/expected/appdemo01/src/router/index.js
📚 Learning: 2025-02-17T12:11:22.718Z
Learnt from: gene9831
PR: opentiny/tiny-engine#1117
File: packages/canvas/container/src/components/CanvasViewerSwitcher.vue:96-117
Timestamp: 2025-02-17T12:11:22.718Z
Learning: In CanvasViewerSwitcher.vue, `state.usedHoverState.element` is guaranteed to have a value when `handleClick` is called, making additional error handling unnecessary.

Applied to files:

  • packages/vue-generator/test/testcases/generator/expected/appdemo01/src/views/testCanvasRowCol.vue
📚 Learning: 2025-03-20T07:20:12.221Z
Learnt from: gene9831
PR: opentiny/tiny-engine#1233
File: packages/canvas/container/src/components/CanvasDivider.vue:184-185
Timestamp: 2025-03-20T07:20:12.221Z
Learning: In CanvasDivider.vue, even though state.verLeft and state.horizontalTop already include 'px' suffix, the CSS properties in state.dividerStyle still need to append 'px' again according to gene9831, suggesting that these state variables might be processed differently than expected when used in style binding.

Applied to files:

  • packages/vue-generator/test/testcases/generator/expected/appdemo01/src/views/testCanvasRowCol.vue
📚 Learning: 2025-01-14T06:55:59.692Z
Learnt from: gene9831
PR: opentiny/tiny-engine#1011
File: packages/configurator/src/router-select-configurator/RouterSelectConfigurator.vue:95-98
Timestamp: 2025-01-14T06:55:59.692Z
Learning: The tiny-select component from opentiny/vue library ensures selected options are valid internally, requiring no additional validation in the change handler.

Applied to files:

  • packages/vue-generator/test/testcases/generator/expected/appdemo01/src/views/createVm.vue
  • packages/vue-generator/test/testcases/generator/mockData.js
🧬 Code graph analysis (5)
packages/vue-generator/test/testcases/data-binding/advanced.test.js (2)
packages/vue-generator/src/generator/generateApp.js (1)
  • generateApp (23-82)
packages/vue-generator/test/testcases/data-binding/mockData.js (2)
  • advancedDataBindingSchema (92-247)
  • advancedDataBindingSchema (92-247)
packages/vue-generator/test/testcases/data-binding/basic.test.js (2)
packages/vue-generator/src/generator/generateApp.js (1)
  • generateApp (23-82)
packages/vue-generator/test/testcases/data-binding/mockData.js (2)
  • basicDataBindingSchema (6-89)
  • basicDataBindingSchema (6-89)
packages/vue-generator/test/testcases/lifecycle/lifecycle.test.js (2)
packages/vue-generator/src/generator/generateApp.js (1)
  • generateApp (23-82)
packages/vue-generator/test/testcases/lifecycle/mockData.js (4)
  • lifecycleTestSchema (6-76)
  • lifecycleTestSchema (6-76)
  • complexLifecycleSchema (79-164)
  • complexLifecycleSchema (79-164)
packages/vue-generator/test/testcases/data-binding/form.test.js (2)
packages/vue-generator/src/generator/generateApp.js (1)
  • generateApp (23-82)
packages/vue-generator/test/testcases/data-binding/mockData.js (2)
  • complexFormDataBindingSchema (331-451)
  • complexFormDataBindingSchema (331-451)
packages/vue-generator/test/testcases/data-binding/edge-case.test.js (2)
packages/vue-generator/src/generator/generateApp.js (1)
  • generateApp (23-82)
packages/vue-generator/test/testcases/data-binding/mockData.js (2)
  • edgeCaseDataBindingSchema (250-328)
  • edgeCaseDataBindingSchema (250-328)
🔇 Additional comments (5)
packages/vue-generator/test/testcases/generator/expected/appdemo01/src/views/testCanvasRowCol.vue (1)

43-46: Nested reactive state shape is clear and future-proof.

The canvasConfig and uiState split makes downstream bindings straightforward. Initializing selectedElement as null is appropriate for tri-state logic (unset vs. selected).

packages/vue-generator/test/testcases/lifecycle/lifecycle.test.js (1)

63-65: This expectation assumes an error identifier is present; ensure schema fix or relax assertion.

Once mockData.js is fixed to declare the error parameter, these assertions are fine. If not, consider loosening to only assert the hook registration and handler names.

-    expect(content).toContain('handleError(error)')
-    expect(content).toContain('reportError(error)')
+    expect(content).toMatch(/handleError\s*\(/)
+    expect(content).toMatch(/reportError\s*\(/)
packages/vue-generator/test/testcases/generator/expected/appdemo01/src/router/index.js (1)

31-36: Route wiring and component existence verified—Approved

The LifeCyclePage.vue component is present in packages/vue-generator/test/testcases/generator/expected/appdemo01/src/views, and its use of <script setup> is valid in Vue 3 (no export default required). The router entry in packages/vue-generator/test/testcases/generator/expected/appdemo01/src/router/index.js lines 31–36 correctly references this component by name, path, and import.

Files to confirm:

  • packages/vue-generator/test/testcases/generator/expected/appdemo01/src/router/index.js (lines 31–36)
  • packages/vue-generator/test/testcases/generator/expected/appdemo01/src/views/LifeCyclePage.vue

Route wiring for lifecycle page looks correct

  • Name/path/component align with the new view and keep the existing router shape (no catch-all, no root prefix changes), consistent with project learnings.
packages/vue-generator/test/testcases/generator/mockData.js (1)

911-941: Good shift to centralized, nested reactive state for bindings

Using formData/inputValues/selectValues clarifies ownership of fields and simplifies v-model wiring in generated pages.

packages/vue-generator/test/testcases/data-binding/advanced.test.js (1)

48-60: Alias '@' Mapping Verified
The resolve.alias block in packages/vue-generator/vite.config.js already maps '@' to ./src (lines 24–28), ensuring imports like @/generator/generateApp resolve correctly in Vitest. No further changes are needed.

Comment on lines +71 to +78
// 验证HTML结构和样式类
expect(content).toContain('<div class="complex-form">')
expect(content).toContain('class="user-section"')
expect(content).toContain('class="preferences-section"')

// 验证嵌套div结构
expect(content).toMatch(/<div[^>]*class="user-section"[^>]*>[\s\S]*<tiny-input[\s\S]*<\/div>/)
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Make tag assertion resilient to form/div difference

Schema uses componentName: 'form'; assertion currently hard-codes <div>. Accept either <form> or <div> with the same class.

-    expect(content).toContain('<div class="complex-form">')
+    expect(content).toMatch(/<(div|form)[^>]*class="complex-form"/)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// 验证HTML结构和样式类
expect(content).toContain('<div class="complex-form">')
expect(content).toContain('class="user-section"')
expect(content).toContain('class="preferences-section"')
// 验证嵌套div结构
expect(content).toMatch(/<div[^>]*class="user-section"[^>]*>[\s\S]*<tiny-input[\s\S]*<\/div>/)
})
// 验证HTML结构和样式类
expect(content).toMatch(/<(div|form)[^>]*class="complex-form"/)
expect(content).toContain('class="user-section"')
expect(content).toContain('class="preferences-section"')
🤖 Prompt for AI Agents
packages/vue-generator/test/testcases/data-binding/form.test.js around lines 71
to 78: the test currently hard-codes <div> tag assertions but schema may render
a <form>; update assertions to accept either <div> or <form>. Replace the
literal '<div class="complex-form">' assertion with a match that accepts either
tag (e.g. use a regex like /<(?:div|form)[^>]*class="complex-form"/), keep the
class existence checks as-is, and change the nested structure assertion to use a
regex that allows <div> or <form> for the user-section tag (e.g.
/<(?:div|form)[^>]*class="user-section"[^>]*>[\s\S]*<tiny-input[\s\S]*<\/(?:div|form)>/).
Ensure tests still assert class names but use toMatch with those regexes so both
tag variants pass.

Comment on lines +163 to +183
componentName: 'TinyRadio',
props: {
value: 'male',
checked: {
type: 'JSExpression',
model: true,
value: 'state.formData.gender'
}
}
},
{
componentName: 'TinyRadio',
props: {
value: 'female',
checked: {
type: 'JSExpression',
model: true,
value: 'state.formData.gender'
}
}
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Use modelValue instead of checked for TinyRadio v-model

For TinyRadio, binding checked to a shared string field is semantically off. Radios should bind the selected value via v-model/modelValue on each radio, with value indicating the option. This avoids boolean coercion and aligns with Vue and component semantics.

Apply this diff to both radio entries:

         {
           componentName: 'TinyRadio',
           props: {
             value: 'male',
-            checked: {
+            modelValue: {
               type: 'JSExpression',
               model: true,
               value: 'state.formData.gender'
             }
           }
         },
         {
           componentName: 'TinyRadio',
           props: {
             value: 'female',
-            checked: {
+            modelValue: {
               type: 'JSExpression',
               model: true,
               value: 'state.formData.gender'
             }
           }
         },
🤖 Prompt for AI Agents
In packages/vue-generator/test/testcases/data-binding/mockData.js around lines
163 to 183, the TinyRadio entries use a `checked` JSExpression bound to the
shared string field; replace `checked` with `modelValue` on each radio so
v-model semantics are used: keep the existing `value` keys, and change the
`checked: { type: 'JSExpression', model: true, value: 'state.formData.gender' }`
objects for both radio entries to `modelValue: { type: 'JSExpression', model:
true, value: 'state.formData.gender' }` so each radio binds the selected value
correctly.

Comment on lines +420 to +429
componentName: 'TinyCheckbox',
props: {
label: '接收邮件通知',
checked: {
type: 'JSExpression',
model: true,
value: 'state.user.preferences.emailNotifications'
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Use modelValue instead of checked for TinyCheckbox v-model

Library components typically expose modelValue for v-model. Using checked here risks producing a non-standard binding shape.

             props: {
               label: '接收邮件通知',
-              checked: {
+              modelValue: {
                 type: 'JSExpression',
                 model: true,
                 value: 'state.user.preferences.emailNotifications'
               }
             }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
componentName: 'TinyCheckbox',
props: {
label: '接收邮件通知',
checked: {
type: 'JSExpression',
model: true,
value: 'state.user.preferences.emailNotifications'
}
}
}
componentName: 'TinyCheckbox',
props: {
label: '接收邮件通知',
modelValue: {
type: 'JSExpression',
model: true,
value: 'state.user.preferences.emailNotifications'
}
}
}
🤖 Prompt for AI Agents
packages/vue-generator/test/testcases/data-binding/mockData.js around lines 420
to 429: the TinyCheckbox prop uses a non-standard "checked" key for v-model;
change the prop key from "checked" to "modelValue" while preserving type:
'JSExpression', model: true and the value
'state.user.preferences.emailNotifications' so the generated binding uses
modelValue for v-model with the same expression.

Comment on lines +14 to +15
"@opentiny/vue": "3.24.0",
"@opentiny/vue-icon": "3.24.0",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Version bump to @opentiny/vue 3.24.0 and @opentiny/vue-icon 3.24.0 looks good; please align schema component versions for consistency.

  • Good move pinning exact versions in an expected fixture to keep snapshots deterministic.
  • For coherence across the test suite and to avoid confusion when reading generated imports, consider updating lifecycle/data-binding mock schemas that still declare '^3.10.0' to '^3.24.0' (see lifecycle/mockData.js in this PR). This does not affect runtime here but reduces drift between fixture deps and schema-declared component versions.

Run this quick scan to spot older declared versions in test schemas:


🏁 Script executed:

#!/bin/bash
rg -nP "@opentiny/vue[\"']?\\s*[:,]\\s*[\"']\\^?3\\.10\\.0[\"']|@opentiny/vue-icon[\"']?\\s*[:,]\\s*[\"']\\^?3\\.10\\.0[\"']" packages/vue-generator/test -C2

Length of output: 906


Align schema mock versions to 3.24.0

I scanned the packages/vue-generator/test suite for any remaining ^3.10.0 declarations and found the following outdated fixture:

  • packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/package.json
    Lines 14–15:
  • "@opentiny/vue": "^3.10.0",
  • "@opentiny/vue-icon": "^3.10.0",
  • "@opentiny/vue": "3.24.0",
  • "@opentiny/vue-icon": "3.24.0",

No other instances of `^3.10.0` were detected in the test schemas. Updating these ensures all expected fixtures remain in lockstep with the new `3.24.0` pinning for deterministic snapshots.

<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion
 "@opentiny/vue": "3.24.0",
 "@opentiny/vue-icon": "3.24.0",
🤖 Prompt for AI Agents
In
packages/vue-generator/test/testcases/generator/expected/appdemo01/package.json
around lines 14-15, the fixture lists @opentiny/vue and @opentiny/vue-icon with
the outdated ^3.10.0 pinning; update both version strings to 3.24.0 (remove the
caret if present) so the test fixture matches the new 3.24.0 deterministic
snapshots and run the tests to validate no other fixtures need changes.

Comment on lines +12 to +13
<tiny-button type="success" @click="refreshData">刷新数据</tiny-button></tiny-card
>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix malformed closing tag for TinyCard

The closing tag is split across lines, which is fragile and may break template parsing in stricter tooling.

Apply this diff:

-      <tiny-button type="success" @click="refreshData">刷新数据</tiny-button></tiny-card
-    >
+      <tiny-button type="success" @click="refreshData">刷新数据</tiny-button>
+    </tiny-card>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<tiny-button type="success" @click="refreshData">刷新数据</tiny-button></tiny-card
>
<tiny-button type="success" @click="refreshData">刷新数据</tiny-button>
</tiny-card>
🤖 Prompt for AI Agents
In
packages/vue-generator/test/testcases/generator/expected/appdemo01/src/views/LifeCyclePage.vue
around lines 12 to 13, the TinyCard closing tag is split across lines
("</tiny-card\n    >"), which can break template parsing; replace the malformed
split closing tag with a single proper closing tag ("</tiny-card>") so the
element is closed on one line and the template remains valid.

Comment on lines +41 to +45
const startAutoRefresh = wrap(function startAutoRefresh() {
if (this.autoRefresh) {
this.refreshInterval = setInterval(() => this.refreshData(), 30000)
}
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

⚠️ Potential issue

Use state-qualified flag and guard against duplicate intervals; restart on activation

  • startAutoRefresh checks this.autoRefresh but the flag lives under state; this prevents auto-refresh from ever starting.
  • When the page is wrapped with KeepAlive, calling startAutoRefresh in both onMounted and onActivated can spawn multiple intervals unless guarded.
  • onActivated should restart auto-refresh after deactivation.

Apply this diff:

-const startAutoRefresh = wrap(function startAutoRefresh() {
-  if (this.autoRefresh) {
-    this.refreshInterval = setInterval(() => this.refreshData(), 30000)
-  }
-})
+const startAutoRefresh = wrap(function startAutoRefresh() {
+  if (!this.state.autoRefresh) return
+  if (this.refreshInterval) return
+  this.refreshInterval = setInterval(() => this.refreshData(), 30000)
+})

 vue.onActivated(
   wrap(function onActivated() {
     console.log('页面激活')
     refreshData()
+    startAutoRefresh()
   })
 )

Also applies to: 77-82

🤖 Prompt for AI Agents
In
packages/vue-generator/test/testcases/generator/expected/appdemo01/src/views/LifeCyclePage.vue
around lines 41-45 (also apply same changes to lines 77-82): startAutoRefresh
currently checks this.autoRefresh but the flag lives under this.state, and it
can create duplicate intervals when called from both onMounted and onActivated;
update the function to read this.state.autoRefresh, guard against creating
another interval if this.refreshInterval is already set, and ensure onActivated
restarts the interval by calling startAutoRefresh after clearing/stopping any
existing interval on deactivation. Ensure refreshInterval is a single source
(clearInterval when stopping), and only setInterval when state.autoRefresh is
true and no existing interval exists.

Comment on lines +2191 to +2325
{
componentName: 'div',
fileName: 'LifeCyclePage',
meta: {
id: 'lifecycle-page',
isPage: true,
parentId: '0',
router: '/lifecycle'
},
props: {
class: 'lifecycle-container'
},
children: [
{
componentName: 'TinyCard',
props: {
title: '仪表板'
},
children: [
{
componentName: 'div',
props: {
class: 'metrics-grid'
},
children: [
{
componentName: 'div',
props: {
class: 'metric-item'
},
children: [
{
componentName: 'Text',
props: {
text: {
type: 'JSExpression',
value: '"活跃用户: " + this.state.activeUsers'
}
}
}
]
},
{
componentName: 'div',
props: {
class: 'metric-item'
},
children: [
{
componentName: 'Text',
props: {
text: {
type: 'JSExpression',
value: '"系统负载: " + this.state.systemLoad + "%"'
}
}
}
]
}
]
},
{
componentName: 'TinyButton',
props: {
type: 'success',
onClick: {
type: 'JSExpression',
value: 'this.refreshData'
}
},
children: ['刷新数据']
}
]
}
],
state: {
activeUsers: 0,
systemLoad: 0,
lastUpdateTime: null,
autoRefresh: true
},
methods: {
refreshData: {
type: 'JSFunction',
value: 'function refreshData() { this.fetchMetrics(); this.state.lastUpdateTime = new Date(); }'
},
fetchMetrics: {
type: 'JSFunction',
value:
'function fetchMetrics() { this.state.activeUsers = Math.floor(Math.random() * 1000); this.state.systemLoad = Math.floor(Math.random() * 100); }'
},
startAutoRefresh: {
type: 'JSFunction',
value:
'function startAutoRefresh() { if (this.autoRefresh) { this.refreshInterval = setInterval(() => this.refreshData(), 30000); } }'
},
stopAutoRefresh: {
type: 'JSFunction',
value:
'function stopAutoRefresh() { if (this.refreshInterval) { clearInterval(this.refreshInterval); this.refreshInterval = null; } }'
},
handleVisibilityChange: {
type: 'JSFunction',
value:
'function handleVisibilityChange() { if (document.hidden) { this.stopAutoRefresh(); } else { this.startAutoRefresh(); } }'
}
},
lifeCycles: {
onMounted: {
type: 'JSFunction',
value:
'function onMounted() { console.log("onMounted."); refreshData(); startAutoRefresh(); document.addEventListener("visibilitychange", handleVisibilityChange) }'
},
onUnmounted: {
type: 'JSFunction',
value:
'function onUnmounted() { console.log("onUnmounted."); stopAutoRefresh(); document.removeEventListener("visibilitychange", handleVisibilityChange) }'
},
onActivated: {
type: 'JSFunction',
value: 'function onActivated() { console.log("页面激活"); refreshData() }'
},
onDeactivated: {
type: 'JSFunction',
value: 'function onDeactivated() { console.log("页面停用"); stopAutoRefresh() }'
},
onUpdated: {
type: 'JSFunction',
value: 'function onUpdated() { console.log("页面已更新", this.state.loginCount) }'
},
onBeforeMount: {
type: 'JSFunction',
value: 'function onBeforeMount() { console.log("onBeforeMount.") }'
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Lifecycle page schema mirrors the SFC issues: fix autoRefresh access, duplicate intervals, and missing restart; correct onUpdated log

Align the schema functions with the SFC fixes so generated output and tests stay in sync.

Apply this diff:

         startAutoRefresh: {
           type: 'JSFunction',
-          value:
-            'function startAutoRefresh() { if (this.autoRefresh) { this.refreshInterval = setInterval(() => this.refreshData(), 30000); } }'
+          value:
+            'function startAutoRefresh() { if (!this.state.autoRefresh) { return; } if (this.refreshInterval) { return; } this.refreshInterval = setInterval(() => this.refreshData(), 30000); }'
         },
@@
         onActivated: {
           type: 'JSFunction',
-          value: 'function onActivated() { console.log("页面激活"); refreshData() }'
+          value: 'function onActivated() { console.log("页面激活"); refreshData(); startAutoRefresh() }'
         },
@@
         onUpdated: {
           type: 'JSFunction',
-          value: 'function onUpdated() { console.log("页面已更新", this.state.loginCount) }'
+          value: 'function onUpdated() { console.log("页面已更新", { activeUsers: this.state.activeUsers, systemLoad: this.state.systemLoad }) }'
         },

Run to locate any remaining this.autoRefresh references that should be this.state.autoRefresh:


🏁 Script executed:

#!/bin/bash
rg -nP "this\.autoRefresh" packages/vue-generator/test -C2

Length of output: 1269


Align schema and expected output with updated SFC lifecycle logic

The test‐schema in mockData.js and the expected Vue file still reference this.autoRefresh and omit duplicate‐interval guards, restarting on activation, and a correct update log. Update both so that:

  • startAutoRefresh guards on !this.state.autoRefresh and prevents multiple intervals
  • onActivated restarts the auto‐refresh
  • onUpdated logs the actual metrics (activeUsers, systemLoad)

Files to update:

• packages/vue-generator/test/testcases/generator/mockData.js
• packages/vue-generator/test/testcases/generator/expected/appdemo01/src/views/LifeCyclePage.vue

Apply these diffs:

--- mockData.js
+++ mockData.js
@@ methods: {
   startAutoRefresh: {
     type: 'JSFunction',
-    value:
-      'function startAutoRefresh() { if (this.autoRefresh) { this.refreshInterval = setInterval(() => this.refreshData(), 30000); } }'
+    value:
+      'function startAutoRefresh() { if (!this.state.autoRefresh) { return; } if (this.refreshInterval) { return; } this.refreshInterval = setInterval(() => this.refreshData(), 30000); }'
   },
@@ lifeCycles: {
   onActivated: {
     type: 'JSFunction',
-    value: 'function onActivated() { console.log("页面激活"); refreshData() }'
+    value: 'function onActivated() { console.log("页面激活"); this.refreshData(); this.startAutoRefresh() }'
   },
@@
   onUpdated: {
     type: 'JSFunction',
-    value: 'function onUpdated() { console.log("页面已更新", this.state.loginCount) }'
+    value: 'function onUpdated() { console.log("页面已更新", { activeUsers: this.state.activeUsers, systemLoad: this.state.systemLoad }) }'
   },
--- expected/appdemo01/src/views/LifeCyclePage.vue
+++ expected/appdemo01/src/views/LifeCyclePage.vue
@@ const startAutoRefresh = wrap(function startAutoRefresh() {
-  if (this.autoRefresh) {
-    this.refreshInterval = setInterval(() => this.refreshData(), 30000)
-  }
+  if (!this.state.autoRefresh) {
+    return
+  }
+  if (this.refreshInterval) {
+    return
+  }
+  this.refreshInterval = setInterval(() => this.refreshData(), 30000)
@@
- onActivated() {
-   console.log("页面激活")
-   this.refreshData()
- },
+ onActivated() {
+   console.log("页面激活")
+   this.refreshData()
+   this.startAutoRefresh()
+ },
@@
- onUpdated() {
-   console.log("页面已更新", this.state.loginCount)
- },
+ onUpdated() {
+   console.log("页面已更新", { activeUsers: this.state.activeUsers, systemLoad: this.state.systemLoad })
+ },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{
componentName: 'div',
fileName: 'LifeCyclePage',
meta: {
id: 'lifecycle-page',
isPage: true,
parentId: '0',
router: '/lifecycle'
},
props: {
class: 'lifecycle-container'
},
children: [
{
componentName: 'TinyCard',
props: {
title: '仪表板'
},
children: [
{
componentName: 'div',
props: {
class: 'metrics-grid'
},
children: [
{
componentName: 'div',
props: {
class: 'metric-item'
},
children: [
{
componentName: 'Text',
props: {
text: {
type: 'JSExpression',
value: '"活跃用户: " + this.state.activeUsers'
}
}
}
]
},
{
componentName: 'div',
props: {
class: 'metric-item'
},
children: [
{
componentName: 'Text',
props: {
text: {
type: 'JSExpression',
value: '"系统负载: " + this.state.systemLoad + "%"'
}
}
}
]
}
]
},
{
componentName: 'TinyButton',
props: {
type: 'success',
onClick: {
type: 'JSExpression',
value: 'this.refreshData'
}
},
children: ['刷新数据']
}
]
}
],
state: {
activeUsers: 0,
systemLoad: 0,
lastUpdateTime: null,
autoRefresh: true
},
methods: {
refreshData: {
type: 'JSFunction',
value: 'function refreshData() { this.fetchMetrics(); this.state.lastUpdateTime = new Date(); }'
},
fetchMetrics: {
type: 'JSFunction',
value:
'function fetchMetrics() { this.state.activeUsers = Math.floor(Math.random() * 1000); this.state.systemLoad = Math.floor(Math.random() * 100); }'
},
startAutoRefresh: {
type: 'JSFunction',
value:
'function startAutoRefresh() { if (this.autoRefresh) { this.refreshInterval = setInterval(() => this.refreshData(), 30000); } }'
},
stopAutoRefresh: {
type: 'JSFunction',
value:
'function stopAutoRefresh() { if (this.refreshInterval) { clearInterval(this.refreshInterval); this.refreshInterval = null; } }'
},
handleVisibilityChange: {
type: 'JSFunction',
value:
'function handleVisibilityChange() { if (document.hidden) { this.stopAutoRefresh(); } else { this.startAutoRefresh(); } }'
}
},
lifeCycles: {
onMounted: {
type: 'JSFunction',
value:
'function onMounted() { console.log("onMounted."); refreshData(); startAutoRefresh(); document.addEventListener("visibilitychange", handleVisibilityChange) }'
},
onUnmounted: {
type: 'JSFunction',
value:
'function onUnmounted() { console.log("onUnmounted."); stopAutoRefresh(); document.removeEventListener("visibilitychange", handleVisibilityChange) }'
},
onActivated: {
type: 'JSFunction',
value: 'function onActivated() { console.log("页面激活"); refreshData() }'
},
onDeactivated: {
type: 'JSFunction',
value: 'function onDeactivated() { console.log("页面停用"); stopAutoRefresh() }'
},
onUpdated: {
type: 'JSFunction',
value: 'function onUpdated() { console.log("页面已更新", this.state.loginCount) }'
},
onBeforeMount: {
type: 'JSFunction',
value: 'function onBeforeMount() { console.log("onBeforeMount.") }'
}
}
methods: {
// … other methods …
startAutoRefresh: {
type: 'JSFunction',
value:
'function startAutoRefresh() { if (!this.state.autoRefresh) { return; } if (this.refreshInterval) { return; } this.refreshInterval = setInterval(() => this.refreshData(), 30000); }'
},
// … other methods …
},
lifeCycles: {
// … other lifecycle hooks …
onActivated: {
type: 'JSFunction',
value: 'function onActivated() { console.log("页面激活"); this.refreshData(); this.startAutoRefresh() }'
},
onUpdated: {
type: 'JSFunction',
value: 'function onUpdated() { console.log("页面已更新", { activeUsers: this.state.activeUsers, systemLoad: this.state.systemLoad }) }'
},
// … other lifecycle hooks …
}

Comment on lines +24 to +31
expect(content).toContain("console.log('Component mounted')")
expect(content).toContain('vue.onUnmounted(')
expect(content).toContain("console.log('Component unmounted')")
expect(content).toContain('vue.onUpdated(')
expect(content).toContain("console.log('Component updated')")
expect(content).toContain('vue.onBeforeMount(')
expect(content).toContain("console.log('Before mount')")

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Assertions are brittle on quote style; prefer regex to accept single/double quotes.

Formatting may switch between ' and " depending on prettier/eslint config. Use regex to avoid false negatives.

-    expect(content).toContain("console.log('Component mounted')")
+    expect(content).toMatch(/console\.log\(['"]Component mounted['"]\)/)
-    expect(content).toContain("console.log('Component unmounted')")
+    expect(content).toMatch(/console\.log\(['"]Component unmounted['"]\)/)
-    expect(content).toContain("console.log('Component updated')")
+    expect(content).toMatch(/console\.log\(['"]Component updated['"]\)/)
-    expect(content).toContain("console.log('Before mount')")
+    expect(content).toMatch(/console\.log\(['"]Before mount['"]\)/)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
expect(content).toContain("console.log('Component mounted')")
expect(content).toContain('vue.onUnmounted(')
expect(content).toContain("console.log('Component unmounted')")
expect(content).toContain('vue.onUpdated(')
expect(content).toContain("console.log('Component updated')")
expect(content).toContain('vue.onBeforeMount(')
expect(content).toContain("console.log('Before mount')")
expect(content).toMatch(/console\.log\(['"]Component mounted['"]\)/)
expect(content).toContain('vue.onUnmounted(')
expect(content).toMatch(/console\.log\(['"]Component unmounted['"]\)/)
expect(content).toContain('vue.onUpdated(')
expect(content).toMatch(/console\.log\(['"]Component updated['"]\)/)
expect(content).toContain('vue.onBeforeMount(')
expect(content).toMatch(/console\.log\(['"]Before mount['"]\)/)
🤖 Prompt for AI Agents
In packages/vue-generator/test/testcases/lifecycle/lifecycle.test.js around
lines 24 to 31, the assertions compare exact string quotes which makes the test
brittle; replace the direct toContain checks with regex-based assertions that
accept either single or double quotes (e.g., use RegExp objects or
expect(content).toMatch(/pattern/)) for each lifecycle log and hook check so
tests pass regardless of quote style; update each expect to use a regex that
matches both ' and " variants while keeping the same text being matched.

Comment on lines +80 to +99
test('should handle lifecycle in component props', async () => {
const instance = generateApp()
const res = await instance.generate(lifecycleTestSchema)
const { genResult, errors } = res

expect(errors).toHaveLength(0)

const vueFile = genResult.find((file) => file.fileName === 'LifecycleTestPage.vue')
expect(vueFile).toBeDefined()

const content = vueFile.fileContent

// 验证生命周期钩子不会作为属性绑定到组件上
expect(content).not.toContain('onMounted=')
expect(content).not.toContain(':onMounted=')

// 而是应该在script中
expect(content).toContain('vue.onMounted(')
expect(content).toContain("console.log('Component mounted')")
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Exercise mixedLifecycleSchema to validate prop-level lifecycle hooks aren’t emitted as attributes.

Currently this test reuses lifecycleTestSchema, which doesn’t define lifecycle hooks on child component props. Switching to mixedLifecycleSchema strengthens coverage and intention.

-import { lifecycleTestSchema, complexLifecycleSchema } from './mockData'
+import { lifecycleTestSchema, complexLifecycleSchema, mixedLifecycleSchema } from './mockData'
@@
-  test('should handle lifecycle in component props', async () => {
+  test('should handle lifecycle in component props', async () => {
     const instance = generateApp()
-    const res = await instance.generate(lifecycleTestSchema)
+    const res = await instance.generate(mixedLifecycleSchema)
     const { genResult, errors } = res
@@
-    const vueFile = genResult.find((file) => file.fileName === 'LifecycleTestPage.vue')
+    const vueFile = genResult.find((file) => file.fileName === 'MixedLifecyclePage.vue')
@@
-    // 验证生命周期钩子不会作为属性绑定到组件上
+    // 验证生命周期钩子不会作为属性绑定到组件上
     expect(content).not.toContain('onMounted=')
     expect(content).not.toContain(':onMounted=')
 
-    // 而是应该在script中
-    expect(content).toContain('vue.onMounted(')
-    expect(content).toContain("console.log('Component mounted')")
+    // 而是应该在script中(并触发子组件对应方法)
+    expect(content).toContain('vue.onMounted(')
+    expect(content).toMatch(/focusInput\s*\(\)/)
+    // 同时不应出现 onBeforeMount 作为属性
+    expect(content).not.toContain('onBeforeMount=')
+    expect(content).not.toContain(':onBeforeMount=')
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
test('should handle lifecycle in component props', async () => {
const instance = generateApp()
const res = await instance.generate(lifecycleTestSchema)
const { genResult, errors } = res
expect(errors).toHaveLength(0)
const vueFile = genResult.find((file) => file.fileName === 'LifecycleTestPage.vue')
expect(vueFile).toBeDefined()
const content = vueFile.fileContent
// 验证生命周期钩子不会作为属性绑定到组件上
expect(content).not.toContain('onMounted=')
expect(content).not.toContain(':onMounted=')
// 而是应该在script中
expect(content).toContain('vue.onMounted(')
expect(content).toContain("console.log('Component mounted')")
})
++ packages/vue-generator/test/testcases/lifecycle/lifecycle.test.js
@@
import { lifecycleTestSchema, complexLifecycleSchema, mixedLifecycleSchema } from './mockData'
@@
test('should handle lifecycle in component props', async () => {
const instance = generateApp()
const res = await instance.generate(mixedLifecycleSchema)
const { genResult, errors } = res
@@
const vueFile = genResult.find((file) => file.fileName === 'MixedLifecyclePage.vue')
@@
const content = vueFile.fileContent
// 验证生命周期钩子不会作为属性绑定到组件上
expect(content).not.toContain('onMounted=')
expect(content).not.toContain(':onMounted=')
- // 而是应该在script中
- expect(content).toContain('vue.onMounted(')
// 而是应该在script中(并触发子组件对应方法)
expect(content).toContain('vue.onMounted(')
expect(content).toMatch(/focusInput\s*\(\)/)
// 同时不应出现 onBeforeMount 作为属性
expect(content).not.toContain('onBeforeMount=')
expect(content).not.toContain(':onBeforeMount=')
})
🤖 Prompt for AI Agents
packages/vue-generator/test/testcases/lifecycle/lifecycle.test.js around lines
80 to 99: the test currently calls instance.generate(lifecycleTestSchema) but
should exercise mixedLifecycleSchema to validate prop-level lifecycle hooks
aren’t emitted as attributes; change the generate call to use
mixedLifecycleSchema (and update any variable names if needed), and if
mixedLifecycleSchema isn’t imported in this test file, add the import at the top
so the test uses the intended schema.

Comment on lines +153 to +156
onErrorCaptured: {
type: 'JSFunction',
value: 'function onErrorCaptured() { handleError(error); reportError(error) }'
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Bug: onErrorCaptured uses undeclared variable “error”. Pass the parameter explicitly.

onErrorCaptured receives the error as its first parameter; the current function references error without declaring it, which would throw at runtime in generated code.

Apply this patch:

-        onErrorCaptured: {
-          type: 'JSFunction',
-          value: 'function onErrorCaptured() { handleError(error); reportError(error) }'
-        }
+        onErrorCaptured: {
+          type: 'JSFunction',
+          value: 'function onErrorCaptured(error) { handleError(error); reportError(error) }'
+        }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
onErrorCaptured: {
type: 'JSFunction',
value: 'function onErrorCaptured() { handleError(error); reportError(error) }'
}
onErrorCaptured: {
type: 'JSFunction',
value: 'function onErrorCaptured(error) { handleError(error); reportError(error) }'
}
🤖 Prompt for AI Agents
In packages/vue-generator/test/testcases/lifecycle/mockData.js around lines 153
to 156, the onErrorCaptured function references an undeclared variable "error";
change the function signature to accept the error parameter (e.g. function
onErrorCaptured(error) { ... }) and use that parameter in the body
(handleError(error); reportError(error)) so the generated code receives the
error value instead of referencing an undefined variable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ospp ospp test test of ut/e2e...

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

Morty Proxy This is a proxified and sanitized view of the page, visit original site.