skills/testing/umbraco-test-builders/SKILL.md
JsonModels.Builders for creating test data in Umbraco tests
npx skillsauth add albanist/umbraco_cli umbraco-test-buildersInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
The @umbraco/json-models-builders package provides fluent builder classes for creating Umbraco backoffice models. These builders simplify test data creation with sensible defaults and chainable configuration methods.
@umbraco/json-models-builders/Users/philw/Projects/Umbraco.JsonModels.Buildersnpm install @umbraco/json-models-builders
All builders follow this pattern:
const model = new SomeBuilder()
.withProperty(value) // Configure scalar properties
.withOtherProperty(value) // Chain multiple configurations
.addChild() // Create nested builder
.withChildProperty(value)
.done() // Return to parent builder
.build(); // Generate final object
| Method Pattern | Purpose | Returns |
|----------------|---------|---------|
| withXxx(value) | Set a property | this (for chaining) |
| addXxx() | Add nested builder | Child builder |
| done() | Return to parent | Parent builder |
| build() | Generate final object | The model |
Create document types with properties, groups, and tabs:
import { DocumentTypeBuilder } from '@umbraco/json-models-builders';
const documentType = new DocumentTypeBuilder()
.withName('Article')
.withAlias('article')
.withAllowAsRoot(true)
.withAllowCultureVariation(true)
.addGroup()
.withName('Content')
.addTextBoxProperty()
.withLabel('Title')
.withAlias('title')
.done()
.addRichTextProperty()
.withLabel('Body')
.withAlias('body')
.done()
.done()
.addGroup()
.withName('SEO')
.addTextBoxProperty()
.withLabel('Meta Title')
.withAlias('metaTitle')
.done()
.done()
.build();
Key Methods:
withName(name) - Document type namewithAlias(alias) - Document type aliaswithAllowAsRoot(bool) - Allow at content rootwithAllowCultureVariation(bool) - Enable variantsAsElementType() - Mark as element type (for blocks)addGroup() - Add property groupaddTab() - Add tabwithDefaultTemplate(template) - Set default templateCreate content items:
import { ContentBuilder } from '@umbraco/json-models-builders';
const content = new ContentBuilder()
.withContentTypeAlias('article')
.withAction('publishNew')
.withParent('-1') // Root
.addVariant()
.withName('My Article')
.withCulture('en-US')
.addProperty()
.withAlias('title')
.withValue('Hello World')
.done()
.addProperty()
.withAlias('body')
.withValue('<p>Article content</p>')
.done()
.done()
.build();
Key Methods:
withContentTypeAlias(alias) - Document type aliaswithTemplateAlias(alias) - Template aliaswithAction(action) - 'publishNew', 'save', etc.withParent(parentId) - Parent node IDaddVariant() - Add content variantCreate media items:
import { MediaBuilder } from '@umbraco/json-models-builders';
const media = new MediaBuilder()
.withName('My Image')
.withMediaTypeAlias('Image')
.addProperty()
.withAlias('umbracoFile')
.withValue({ src: '/media/image.jpg' })
.done()
.build();
Create data types:
import { DataTypeBuilder } from '@umbraco/json-models-builders';
const dataType = new DataTypeBuilder()
.withName('My Text Box')
.withSaveNewAction()
.build();
Add properties to document types:
documentTypeBuilder
.addGroup()
.withName('Content')
.addTextBoxProperty()
.withLabel('Title')
.withAlias('title')
.withDescription('Enter the page title')
.withMandatory(true)
.done()
.done()
.addRichTextProperty()
.withLabel('Body Text')
.withAlias('bodyText')
.done()
.addMediaPickerProperty()
.withLabel('Featured Image')
.withAlias('featuredImage')
.done()
.addContentPickerProperty()
.withLabel('Related Page')
.withAlias('relatedPage')
.done()
.addCustomProperty()
.withLabel('Custom Field')
.withAlias('customField')
.withDataTypeId('your-datatype-id')
.done()
import { BlockListDataTypeBuilder } from '@umbraco/json-models-builders';
const blockList = new BlockListDataTypeBuilder()
.withName('Content Blocks')
.addBlock()
.withContentElementTypeKey('hero-block-key')
.withLabel('Hero Block')
.done()
.addBlock()
.withContentElementTypeKey('text-block-key')
.withLabel('Text Block')
.done()
.withMin(1)
.withMax(10)
.withUseLiveEditing(true)
.build();
import { BlockGridDataTypeBuilder } from '@umbraco/json-models-builders';
const blockGrid = new BlockGridDataTypeBuilder()
.withName('Page Grid')
.withGridColumns(12)
.addBlock()
.withContentElementTypeKey('row-block-key')
.withLabel('Row')
.withColumnSpanOptions([6, 12])
.done()
.addBlock()
.withContentElementTypeKey('image-block-key')
.withLabel('Image')
.done()
.build();
import { ContentBuilder, BlockListValueBuilder } from '@umbraco/json-models-builders';
const content = new ContentBuilder()
.withContentTypeAlias('page')
.addVariant()
.withName('Home')
.addProperty()
.withAlias('blocks')
.addBlockListValue()
.addBlockListEntry()
.withContentTypeKey('hero-block-key')
.appendContentProperties('heading', 'Welcome')
.appendContentProperties('subheading', 'To our site')
.done()
.addBlockListEntry()
.withContentTypeKey('text-block-key')
.appendContentProperties('text', 'Some content here')
.done()
.done()
.done()
.done()
.build();
import { UserBuilder } from '@umbraco/json-models-builders';
const user = new UserBuilder()
.withName('Test User')
.withEmail('[email protected]')
.withUserGroups(['admin'])
.build();
import { UserGroupBuilder } from '@umbraco/json-models-builders';
const userGroup = new UserGroupBuilder()
.withName('Editors')
.withAlias('editors')
.withIcon('icon-users')
.appendSection('content')
.appendSection('media')
.addDefaultPermissions()
.withBrowseNode()
.withCreate()
.withUpdate()
.withPublish()
.done()
.withSaveNew()
.build();
userGroupBuilder
.addNodePermissions()
.forNode('content-node-id')
.withBrowseNode()
.withCreate()
.withUpdate()
.withDelete()
.withPublish()
.withUnpublish()
.done()
import { TemplateBuilder } from '@umbraco/json-models-builders';
const template = new TemplateBuilder()
.withName('Article Template')
.withAlias('articleTemplate')
.withContent(`@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage
<h1>@Model.Name</h1>`)
.build();
import { StylesheetBuilder } from '@umbraco/json-models-builders';
const stylesheet = new StylesheetBuilder()
.withName('main.css')
.withContent('body { font-family: sans-serif; }')
.build();
import { ScriptBuilder } from '@umbraco/json-models-builders';
const script = new ScriptBuilder()
.withName('main.js')
.withContent('console.log("Hello");')
.build();
Generate safe aliases from names:
import { AliasHelper } from '@umbraco/json-models-builders';
// Convert to camelCase alias
AliasHelper.toAlias('My Page Type');
// Returns: "myPageType"
// Create safe alias with prefix/suffix
AliasHelper.toSafeAlias('My Page Type');
// Returns: "aMyPageTypea"
// Capitalize first character
AliasHelper.capitalize('hello');
// Returns: "Hello"
// Convert sentence to camelCase
AliasHelper.toCamelCase('My Awesome Example');
// Returns: "myAwesomeExample"
// Convert UUID to alias (removes dashes)
AliasHelper.uuidToAlias('123e4567-e89b-12d3-a456-426614174000');
// Returns: "123e4567e89b12d3a456426614174000"
import { DocumentTypeBuilder, AliasHelper } from '@umbraco/json-models-builders';
const name = 'Blog Post';
const alias = AliasHelper.toAlias(name);
const blogPost = new DocumentTypeBuilder()
.withName(name)
.withAlias(alias)
.withAllowAsRoot(true)
.withAllowCultureVariation(true)
.addGroup()
.withName('Content')
.addTextBoxProperty()
.withLabel('Title')
.withAlias('title')
.withMandatory(true)
.done()
.addTextBoxProperty()
.withLabel('Author')
.withAlias('author')
.done()
.addRichTextProperty()
.withLabel('Body')
.withAlias('body')
.done()
.done()
.addGroup()
.withName('Media')
.addMediaPickerProperty()
.withLabel('Featured Image')
.withAlias('featuredImage')
.done()
.done()
.build();
// Use in test
await umbracoApi.documentType.save(blogPost);
const heroBlock = new DocumentTypeBuilder()
.withName('Hero Block')
.withAlias('heroBlock')
.AsElementType() // Important for blocks!
.addGroup()
.withName('Content')
.addTextBoxProperty()
.withLabel('Heading')
.withAlias('heading')
.done()
.addTextBoxProperty()
.withLabel('Subheading')
.withAlias('subheading')
.done()
.addMediaPickerProperty()
.withLabel('Background Image')
.withAlias('backgroundImage')
.done()
.done()
.build();
const pageContent = new ContentBuilder()
.withContentTypeAlias('landingPage')
.withAction('publishNew')
.addVariant()
.withName('Landing Page')
.addProperty()
.withAlias('pageTitle')
.withValue('Welcome')
.done()
.addProperty()
.withAlias('contentBlocks')
.addBlockListValue()
.addBlockListEntry()
.withContentTypeKey(heroBlockKey)
.appendContentProperties('heading', 'Welcome to Our Site')
.appendContentProperties('subheading', 'Discover what we offer')
.done()
.addBlockListEntry()
.withContentTypeKey(textBlockKey)
.appendContentProperties('text', '<p>More content here...</p>')
.done()
.done()
.done()
.done()
.build();
tools
Umbraco Automate operations (event-driven workflow automation)
development
Webhook management (the Management API's outbound event notifications)
development
Backoffice user management (accounts, state, groups, API credentials)
tools
Backoffice user group management (permission sets)