diff --git a/node_modules/.vite/vitest/results.json b/node_modules/.vite/vitest/results.json
index 8219f2c..a86febf 100644
--- a/node_modules/.vite/vitest/results.json
+++ b/node_modules/.vite/vitest/results.json
@@ -1 +1 @@
-{"version":"1.6.1","results":[[":trmnl-form-builder.test.js",{"duration":4,"failed":false}]]}
\ No newline at end of file
+{"version":"1.6.1","results":[[":tests/fixture.test.js",{"duration":208,"failed":false}],[":tests/unit.test.js",{"duration":304,"failed":false}]]}
\ No newline at end of file
diff --git a/test-fixtures/weather.yml b/test-fixtures/weather.yml
new file mode 100644
index 0000000..12a197a
--- /dev/null
+++ b/test-fixtures/weather.yml
@@ -0,0 +1,35 @@
+- keyname: bio
+ name: About This Plugin
+ field_type: author_bio
+ description: "Get a clear snapshot of today's weather plus a 5-day forecast, including temperatures, uv indexes and rain chances.
This plugin was created by Daniel Sitnik."
+ category: life,personal
+ learn_more_url: https://ko-fi.com/danielsitnik/shop
+ github_url: http://github.com/danielsitnik/
+ email_address: danielsitnik@outlook.com
+- keyname: latitude
+ field_type: string
+ name: Latitude
+ description: The latitude of your location
+ placeholder: Latitude
+ help_text: 'Go to https://www.latlong.net, search for the desired place or navigate using the map, then copy the latitude.'
+- keyname: longitude
+ field_type: string
+ name: Longitude
+ description: The longitude of your location
+ placeholder: Longitude
+ help_text: 'Go to https://www.latlong.net, search for the desired place or navigate using the map, then copy the longitude.'
+- keyname: temp_unit
+ field_type: select
+ name: Temperature Unit
+ description: Select the desired temperature unit
+ default: fahrenheit
+ options:
+ - Celsius: celsius
+ - Fahrenheit: fahrenheit
+- keyname: location_name
+ field_type: string
+ optional: true
+ name: Location Name
+ placeholder: Location name
+ description: "Give a name to help identify this location (eg: home, office, city name, etc.)"
+ help_text: "This name will be displayed at the right side of the plugin title bar."
\ No newline at end of file
diff --git a/tests/fixture.test.js b/tests/fixture.test.js
new file mode 100644
index 0000000..b5fadba
--- /dev/null
+++ b/tests/fixture.test.js
@@ -0,0 +1,75 @@
+import { describe, it, expect, beforeEach } from 'vitest'
+import { readFileSync } from 'fs'
+import { join } from 'path'
+import '../trmnl-form-builder.js'
+
+describe('TRMNL Form Builder - Fixture Tests', () => {
+ let element
+
+ beforeEach(() => {
+ element = document.createElement('trmnl-form-builder')
+ document.body.appendChild(element)
+ })
+
+ describe('Weather Plugin Fixture', () => {
+ it('should parse weather plugin YAML correctly', () => {
+ const yamlContent = readFileSync(
+ join(process.cwd(), 'test-fixtures', 'weather.yml'),
+ 'utf-8'
+ )
+
+ const fields = element.parseYaml(yamlContent)
+
+ // Should have 5 fields
+ expect(fields).toHaveLength(5)
+
+ // Check author_bio field
+ expect(fields[0].keyname).toBe('bio')
+ expect(fields[0].field_type).toBe('author_bio')
+ expect(fields[0].category).toBe('life,personal')
+
+ // Check select field with label:value options
+ const tempUnitField = fields[3]
+ expect(tempUnitField.keyname).toBe('temp_unit')
+ expect(tempUnitField.field_type).toBe('select')
+ expect(tempUnitField.default).toBe('fahrenheit')
+ expect(tempUnitField.options).toHaveLength(2)
+ expect(tempUnitField.options[0]).toEqual({ 'Celsius': 'celsius' })
+ expect(tempUnitField.options[1]).toEqual({ 'Fahrenheit': 'fahrenheit' })
+
+ // Check optional field
+ const locationField = fields[4]
+ expect(locationField.keyname).toBe('location_name')
+ expect(locationField.optional).toBe(true)
+ })
+
+ it('should round-trip weather plugin YAML correctly', () => {
+ const yamlContent = readFileSync(
+ join(process.cwd(), 'test-fixtures', 'weather.yml'),
+ 'utf-8'
+ )
+
+ // Parse the YAML
+ const fields = element.parseYaml(yamlContent)
+
+ // Set fields on element
+ element.fields = fields.map((fieldData, index) => ({
+ id: `field_${index}`,
+ ...fieldData
+ }))
+
+ // Generate YAML back
+ const generatedYaml = element.generateYaml()
+
+ // Parse again
+ const reparsedFields = element.parseYaml(generatedYaml)
+
+ // Should have same number of fields
+ expect(reparsedFields).toHaveLength(fields.length)
+
+ // Check critical properties are preserved
+ expect(reparsedFields[3].options).toEqual(fields[3].options)
+ expect(reparsedFields[4].optional).toBe(fields[4].optional)
+ })
+ })
+})
\ No newline at end of file
diff --git a/tests/unit.test.js b/tests/unit.test.js
new file mode 100644
index 0000000..139a77f
--- /dev/null
+++ b/tests/unit.test.js
@@ -0,0 +1,44 @@
+import { describe, it, expect, beforeEach } from 'vitest'
+import '../trmnl-form-builder.js'
+
+describe('TRMNL Form Builder - Unit Tests', () => {
+ let element
+
+ beforeEach(() => {
+ element = document.createElement('trmnl-form-builder')
+ document.body.appendChild(element)
+ })
+
+ it('should register the custom element', () => {
+ expect(customElements.get('trmnl-form-builder')).toBeDefined()
+ })
+
+ describe('escapeYaml', () => {
+ it('should escape special characters', () => {
+ expect(element.escapeYaml('hello: world')).toBe('"hello: world"')
+ expect(element.escapeYaml('simple')).toBe('simple')
+ expect(element.escapeYaml(123)).toBe('123')
+ expect(element.escapeYaml('')).toBe('""')
+ })
+
+ it('should quote YAML boolean literals', () => {
+ expect(element.escapeYaml('true')).toBe('"true"')
+ expect(element.escapeYaml('false')).toBe('"false"')
+ expect(element.escapeYaml('yes')).toBe('"yes"')
+ expect(element.escapeYaml('no')).toBe('"no"')
+ })
+ })
+
+ describe('unescapeYaml', () => {
+ it('should remove surrounding quotes', () => {
+ expect(element.unescapeYaml('"hello"')).toBe('hello')
+ expect(element.unescapeYaml("'hello'")).toBe('hello')
+ expect(element.unescapeYaml('hello')).toBe('hello')
+ })
+
+ it('should handle non-string values', () => {
+ expect(element.unescapeYaml(123)).toBe(123)
+ expect(element.unescapeYaml(0)).toBe(0)
+ })
+ })
+})
\ No newline at end of file
diff --git a/trmnl-form-builder.test.js b/trmnl-form-builder.test.js
deleted file mode 100644
index 528604b..0000000
--- a/trmnl-form-builder.test.js
+++ /dev/null
@@ -1,8 +0,0 @@
-import { describe, it, expect, beforeAll } from 'vitest'
-import './trmnl-form-builder.js'
-
-describe('TRMNL Form Builder', () => {
- it('should register the custom element', () => {
- expect(customElements.get('trmnl-form-builder')).toBeDefined()
- })
-})
\ No newline at end of file