diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml
index 569e6324d..5051e0957 100644
--- a/.github/workflows/check.yml
+++ b/.github/workflows/check.yml
@@ -114,7 +114,7 @@ jobs:
         run: npm run build
       - name: Execute test-generator
         working-directory: amplify-codegen-ui-staging/packages/test-generator
-        run: node .
+        run: node ./dist/lib/generators/GenerateTestApp.js
       - name: Create test app with dependencies
         working-directory: .
         run: |
diff --git a/packages/test-generator/index.ts b/packages/test-generator/index.ts
index 0a92f29d6..7faaace27 100644
--- a/packages/test-generator/index.ts
+++ b/packages/test-generator/index.ts
@@ -1,83 +1,6 @@
-import { StudioComponent } from '@amzn/amplify-ui-codegen-schema';
-import { StudioTemplateRendererManager, StudioTemplateRendererFactory } from '@amzn/studio-ui-codegen';
-import {
-  AmplifyRenderer,
-  ReactOutputConfig,
-  ReactRenderConfig,
-  ModuleKind,
-  ScriptTarget,
-  ScriptKind,
-} from '@amzn/studio-ui-codegen-react';
-import path from 'path';
-import log from 'loglevel';
-import { ComponentSchemas } from './lib';
+import { TestGenerator } from './lib/generators/TestGenerator';
 
-Error.stackTraceLimit = Infinity;
-
-log.setLevel('info');
-
-const renderConfig: ReactRenderConfig = {
-  module: ModuleKind.CommonJS,
-  target: ScriptTarget.ES2015,
-  script: ScriptKind.TSX,
-};
-
-const componentRendererFactory = new StudioTemplateRendererFactory(
-  (component: StudioComponent) => new AmplifyRenderer(component, renderConfig),
-);
-
-// const themeRendererFactory = new StudioTemplateRendererFactory(
-//   (theme: StudioTheme) => new ReactThemeStudioTemplateRenderer(theme, renderConfig),
-// );
-
-const outputPathDir = path.resolve(path.join(__dirname, '..', 'test-app-templates', 'src', 'ui-components'));
-const outputConfig: ReactOutputConfig = {
-  outputPathDir,
-};
-
-const rendererManager = new StudioTemplateRendererManager(componentRendererFactory, outputConfig);
-// const themeRendererManager = new StudioTemplateRendererManager(themeRendererFactory, outputConfig);
-
-const decorateTypescriptWithMarkdown = (typescriptSource: string): string => {
-  return `\`\`\`typescript jsx\n${typescriptSource}\n\`\`\``;
-};
-
-Object.entries(ComponentSchemas).forEach(([name, schema]) => {
-  log.info(`# ${name}`);
-  try {
-    rendererManager.renderSchemaToTemplate(schema as any);
-    const buildRenderer = componentRendererFactory.buildRenderer(schema as any);
-
-    const compOnly = buildRenderer.renderComponentOnly();
-    log.info('## Component Only Output');
-    log.info('### componentImports');
-    log.info(decorateTypescriptWithMarkdown(compOnly.importsText));
-    log.info('### componentText');
-    log.info(decorateTypescriptWithMarkdown(compOnly.compText));
-
-    const compOnlyAppSample = buildRenderer.renderSampleCodeSnippet();
-    log.info('## Code Snippet Output');
-    log.info('### componentImports');
-    log.info(decorateTypescriptWithMarkdown(compOnlyAppSample.importsText));
-    log.info('### componentText');
-    log.info(decorateTypescriptWithMarkdown(compOnlyAppSample.compText));
-  } catch (err) {
-    log.error(`${name} failed with error:`);
-    log.error(err);
-  }
-});
-//
-// Object.entries(ThemeSchemas).forEach(([name, schema]) => {
-//   log.info(`# ${name}`);
-//   try {
-//     themeRendererManager.renderSchemaToTemplate(schema as any);
-//     const buildRenderer = themeRendererFactory.buildRenderer(schema as any);
-//
-//     const component = buildRenderer.renderComponent();
-//     log.info('## Theme Output');
-//     log.info(decorateTypescriptWithMarkdown(component.componentText));
-//   } catch (err) {
-//     log.error(`${name} failed with error:`);
-//     log.error(err);
-//   }
-// });
+new TestGenerator({
+  writeToLogger: true,
+  writeToDisk: false,
+}).generate();
diff --git a/packages/test-generator/lib/components/boxWithButton.json b/packages/test-generator/lib/components/boxWithButton.json
index 8d4246481..dd3b2662d 100644
--- a/packages/test-generator/lib/components/boxWithButton.json
+++ b/packages/test-generator/lib/components/boxWithButton.json
@@ -8,13 +8,19 @@
       "id": "0987-6543-3211",
       "componentType": "Button",
       "properties": {
-        "color": {
-          "value": "#ff0000"
-        },
-        "width": {
-          "value": "20px"
+      },
+      "children": [
+        {
+          "id": "1234-5678-9010",
+          "componentType": "Text",
+          "name": "CustomText",
+          "properties": {
+            "value": {
+              "value": "Text in Button"
+            }
+          }
         }
-      }
+      ]
     }
   ]
 }
diff --git a/packages/test-generator/lib/components/buttonWithConcatenatedText.json b/packages/test-generator/lib/components/buttonWithConcatenatedText.json
new file mode 100644
index 000000000..bb65948b2
--- /dev/null
+++ b/packages/test-generator/lib/components/buttonWithConcatenatedText.json
@@ -0,0 +1,47 @@
+{
+  "id": "1234-5678-9010",
+  "componentType": "Button",
+  "name": "ButtonWithConcatenatedText",
+  "bindingProperties": {
+    "width": {
+      "type": "Number"
+    },
+    "buttonUser": {
+      "type": "Data",
+      "bindingProperties": {
+        "model": "User"
+      }
+    },
+    "buttonColor": {
+      "type": "String"
+    }
+  },
+  "properties": {
+    "label": {
+      "concat": [
+        {
+          "bindingProperties": {
+            "property": "buttonUser",
+            "field": "firstname"
+          },
+          "defaultValue": "Harry"
+        },
+        {
+          "value": " "
+        },
+        {
+          "bindingProperties": {
+            "property": "buttonUser",
+            "field": "lastname"
+          },
+          "defaultValue": "Callahan"
+        }
+      ]
+    },
+    "labelWidth": {
+      "bindingProperties": {
+        "property": "width"
+      }
+    }
+  }
+}
diff --git a/packages/test-generator/lib/components/buttonWithConditionalState.json b/packages/test-generator/lib/components/buttonWithConditionalState.json
new file mode 100644
index 000000000..3e4938aec
--- /dev/null
+++ b/packages/test-generator/lib/components/buttonWithConditionalState.json
@@ -0,0 +1,105 @@
+{
+  "id": "1234-5678-9010",
+  "componentType": "Button",
+  "name": "ButtonWithConditionalState",
+  "bindingProperties": {
+    "width": {
+      "type": "Number"
+    },
+    "buttonUser": {
+      "type": "Data",
+      "bindingProperties": {
+        "model": "User"
+      }
+    },
+    "buttonColor": {
+      "type": "String"
+    }
+  },
+  "properties": {
+    "label": {
+      "concat": [
+        {
+          "bindingProperties": {
+            "property": "buttonUser",
+            "field": "firstname"
+          },
+          "defaultValue": "Harry"
+        },
+        {
+          "value": " "
+        },
+        {
+          "bindingProperties": {
+            "property": "buttonUser",
+            "field": "lastname"
+          },
+          "defaultValue": "Callahan"
+        }
+      ]
+    },
+    "labelWidth": {
+      "bindingProperties": {
+        "property": "width"
+      }
+    },
+    "disabled": {
+      "condition": {
+        "property": "buttonUser",
+        "field": "isLoggedIn",
+        "operator": "eq",
+        "operand": true,
+        "then": {
+          "value": true
+        },
+        "else": {
+          "value": false
+        }
+      }
+    },
+    "prompt": {
+      "condition": {
+        "property": "buttonUser",
+        "field": "age",
+        "operator": "gt",
+        "operand": 18,
+        "then": {
+          "concat": [
+            {
+              "bindingProperties": {
+                "property": "buttonUser",
+                "field": "firstname"
+              }
+            },
+            {
+              "value": ", cast your vote."
+            }
+          ]
+        },
+        "else": {
+          "value": "Sorry you cannot vote"
+        }
+      }
+    },
+    "backgroundColor": {
+      "condition": {
+        "property": "buttonUser",
+        "field": "isLoggedIn",
+        "operator": "eq",
+        "operand": true,
+        "then": {
+          "bindingProperties": {
+            "property": "buttonUser",
+            "field": "loggedInColor"
+          }
+        },
+        "else": {
+          "bindingProperties": {
+            "property": "buttonUser",
+            "field": "loggedOutColor"
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/packages/test-generator/lib/components/customText.json b/packages/test-generator/lib/components/customText.json
index ed654d941..0850ef56c 100644
--- a/packages/test-generator/lib/components/customText.json
+++ b/packages/test-generator/lib/components/customText.json
@@ -10,7 +10,7 @@
       "value": "20px"
     },
     "value": {
-      "value": "Text Value"
+      "value": "Custom Text Value"
     }
   }
 }
diff --git a/packages/test-generator/lib/components/index.ts b/packages/test-generator/lib/components/index.ts
index 1efd2f093..b5ab450d2 100644
--- a/packages/test-generator/lib/components/index.ts
+++ b/packages/test-generator/lib/components/index.ts
@@ -4,3 +4,5 @@ export { default as CustomButton } from './customButton.json';
 export { default as BoxWithButtonExposedAs } from './boxWithButtonExposedAs.json';
 export { default as CustomText } from './customText.json';
 export { default as TextWithDataBinding } from './textWithDataBinding.json';
+export { default as ButtonWithConcatenatedText } from './buttonWithConcatenatedText.json';
+export { default as ButtonWithConditionalState } from './buttonWithConditionalState.json';
diff --git a/packages/test-generator/lib/generators/GenerateTestApp.ts b/packages/test-generator/lib/generators/GenerateTestApp.ts
new file mode 100644
index 000000000..59a9ac219
--- /dev/null
+++ b/packages/test-generator/lib/generators/GenerateTestApp.ts
@@ -0,0 +1,11 @@
+import { TestGenerator } from './TestGenerator';
+
+new TestGenerator({
+  writeToLogger: false,
+  writeToDisk: true,
+  disabledSchemas: [
+    'ButtonWithConditionalState', // TODO: Fix Conditional
+    'ButtonWithConcatenatedText', // TODO: Fix Concatenation
+    'ExampleTheme', // TODO: Fix Themes
+  ],
+}).generate();
diff --git a/packages/test-generator/lib/generators/TestGenerator.ts b/packages/test-generator/lib/generators/TestGenerator.ts
new file mode 100644
index 000000000..69fbf3659
--- /dev/null
+++ b/packages/test-generator/lib/generators/TestGenerator.ts
@@ -0,0 +1,128 @@
+import { StudioComponent, StudioTheme } from '@amzn/amplify-ui-codegen-schema';
+import { StudioTemplateRendererManager, StudioTemplateRendererFactory } from '@amzn/studio-ui-codegen';
+import {
+  AmplifyRenderer,
+  ReactOutputConfig,
+  ModuleKind,
+  ScriptTarget,
+  ScriptKind,
+  ReactThemeStudioTemplateRenderer,
+  ReactRenderConfig,
+} from '@amzn/studio-ui-codegen-react';
+import path from 'path';
+import log from 'loglevel';
+import { ComponentSchemas, ThemeSchemas } from '../index';
+
+const DEFAULT_RENDER_CONFIG = {
+  module: ModuleKind.CommonJS,
+  target: ScriptTarget.ES2015,
+  script: ScriptKind.TSX,
+};
+
+Error.stackTraceLimit = Infinity;
+
+log.setLevel('info');
+
+export type TestGeneratorParams = {
+  writeToLogger: boolean;
+  writeToDisk: boolean;
+  renderConfigOverride?: ReactRenderConfig;
+  disabledSchemas?: string[];
+};
+
+export class TestGenerator {
+  private readonly params: TestGeneratorParams;
+
+  private readonly componentRendererFactory: any;
+
+  private readonly themeRendererFactory: any;
+
+  private readonly rendererManager: any;
+
+  private readonly themeRendererManager: any;
+
+  constructor(params: TestGeneratorParams) {
+    this.params = params;
+    const mergedRenderConfig = { ...DEFAULT_RENDER_CONFIG, ...params.renderConfigOverride };
+    this.componentRendererFactory = new StudioTemplateRendererFactory(
+      (component: StudioComponent) => new AmplifyRenderer(component, mergedRenderConfig),
+    );
+    this.themeRendererFactory = new StudioTemplateRendererFactory(
+      (theme: StudioTheme) => new ReactThemeStudioTemplateRenderer(theme, mergedRenderConfig),
+    );
+    const outputPathDir = path.resolve(
+      path.join(__dirname, '..', '..', '..', 'test-app-templates', 'src', 'ui-components'),
+    );
+    const outputConfig: ReactOutputConfig = { outputPathDir };
+    this.rendererManager = new StudioTemplateRendererManager(this.componentRendererFactory, outputConfig);
+    this.themeRendererManager = new StudioTemplateRendererManager(this.themeRendererFactory, outputConfig);
+  }
+
+  private decorateTypescriptWithMarkdown = (typescriptSource: string): string => {
+    return `\`\`\`typescript jsx\n${typescriptSource}\n\`\`\``;
+  };
+
+  generate = () => {
+    const renderErrors: { [key: string]: any } = {};
+
+    Object.entries(ComponentSchemas).forEach(([name, schema]) => {
+      if (this.params.disabledSchemas && this.params.disabledSchemas.includes(name)) {
+        return;
+      }
+      try {
+        if (this.params.writeToDisk) {
+          this.rendererManager.renderSchemaToTemplate(schema as any);
+        }
+
+        if (this.params.writeToLogger) {
+          const buildRenderer = this.componentRendererFactory.buildRenderer(schema as any);
+          const compOnly = buildRenderer.renderComponentOnly();
+          const compOnlyAppSample = buildRenderer.renderSampleCodeSnippet();
+          log.info(`# ${name}`);
+          log.info('## Component Only Output');
+          log.info('### componentImports');
+          log.info(this.decorateTypescriptWithMarkdown(compOnly.importsText));
+          log.info('### componentText');
+          log.info(this.decorateTypescriptWithMarkdown(compOnly.compText));
+          log.info('## Code Snippet Output');
+          log.info('### componentImports');
+          log.info(this.decorateTypescriptWithMarkdown(compOnlyAppSample.importsText));
+          log.info('### componentText');
+          log.info(this.decorateTypescriptWithMarkdown(compOnlyAppSample.compText));
+        }
+      } catch (err) {
+        renderErrors[name] = err;
+      }
+    });
+
+    Object.entries(ThemeSchemas).forEach(([name, schema]) => {
+      if (this.params.disabledSchemas && this.params.disabledSchemas.includes(name)) {
+        return;
+      }
+      try {
+        if (this.params.writeToDisk) {
+          this.themeRendererManager.renderSchemaToTemplate(schema as any);
+        }
+
+        if (this.params.writeToLogger) {
+          const buildRenderer = this.themeRendererFactory.buildRenderer(schema as any);
+          const component = buildRenderer.renderComponent();
+          log.info(`# ${name}`);
+          log.info('## Theme Output');
+          log.info(this.decorateTypescriptWithMarkdown(component.componentText));
+        }
+      } catch (err) {
+        renderErrors[name] = err;
+      }
+    });
+
+    if (Object.keys(renderErrors).length > 0) {
+      log.error('Caught exceptions while rendering templates');
+      Object.entries(renderErrors).forEach(([name, error]) => {
+        log.error(`Schema: ${name}`);
+        log.error(error);
+      });
+      throw new Error('Not all tests rendered successfully');
+    }
+  };
+}
diff --git a/packages/test-generator/test-app-templates/cypress/integration/generated-components-spec.js b/packages/test-generator/test-app-templates/cypress/integration/generated-components-spec.js
index a8ab3f78a..81009f1ca 100644
--- a/packages/test-generator/test-app-templates/cypress/integration/generated-components-spec.js
+++ b/packages/test-generator/test-app-templates/cypress/integration/generated-components-spec.js
@@ -1,5 +1,59 @@
-describe('My First Tests', () => {
-  it('Opens the test app', () => {
+describe('Generated Components', () => {
+  describe('Sanity Test', () => {
+    it('Successfully opens the app', () => {
+      cy.visit('http://localhost:3000');
+    });
+  });
+
+  describe('Basic Components', () => {
+    it('Renders Box with Button, and text inside', () => {
+      cy.visit('http://localhost:3000');
+      cy.get('button').contains('Text in Button');
+    });
+
+    it('Renders Text component', () => {
+      cy.visit('http://localhost:3000');
+      cy.contains('Custom Text Value');
+    });
+  });
+
+  describe('Conditional Data', () => {
+    // TODO: Write Conditional Cases
+  });
+
+  describe('Concatenated Data', () => {
+    // TODO: Get Concatenation Cases Working
+    // it('Renders Button text as a concatenated, bound element', () => {
+    //   cy.visit('http://localhost:3000');
+    //   cy.contains('Harry Callahan')
+    // });
+    //
+    // it('Renders Button text as a concatenated, bound element, with overrides', () => {
+    //   cy.visit('http://localhost:3000');
+    //   cy.contains('Norm Gunderson')
+    // });
+  });
+
+  describe('Component Variants', () => {
+    // TODO: Write Variant Cases
+  });
+
+  describe('Data Binding', () => {
+    // TODO: Write Data Binding Cases
+  });
+
+  describe('Action Binding', () => {
+    // TODO: Write Action Binding Cases
+  });
+
+  describe('Collections', () => {
+    // TODO: Write Collection Cases
+  });
+});
+
+describe('Generated Themes', () => {
+  it('Successfully decorates the app', () => {
     cy.visit('http://localhost:3000');
+    // TODO: Write theming test
   });
 });
diff --git a/packages/test-generator/test-app-templates/src/App.tsx b/packages/test-generator/test-app-templates/src/App.tsx
index f44e4d3ad..69242270d 100644
--- a/packages/test-generator/test-app-templates/src/App.tsx
+++ b/packages/test-generator/test-app-templates/src/App.tsx
@@ -6,17 +6,63 @@ import BoxWithButtonExposedAs from './ui-components/BoxWithButtonExposedAs';
 import CustomButton from './ui-components/CustomButton';
 import CustomText from './ui-components/CustomText';
 import TextWithDataBinding from './ui-components/TextWithDataBinding';
+// import ButtonWithConcatenatedText from './ui-components/ButtonWithConcatenatedText';
+// import ButtonWithConditionalState from './ui-components/ButtonWithConditionalState';
 /* eslint-enable import/extensions */
 
 function App() {
   return (
     <>
-      <BoxTest></BoxTest>
-      <BoxWithButton></BoxWithButton>
-      <BoxWithButtonExposedAs></BoxWithButtonExposedAs>
-      <CustomButton></CustomButton>
-      <CustomText></CustomText>
-      <TextWithDataBinding></TextWithDataBinding>
+      <BoxTest />
+      <BoxWithButton />
+      <BoxWithButtonExposedAs />
+      <CustomButton />
+      <CustomText />
+      <TextWithDataBinding />
+      {/*
+      TODO: buttonUser Listed as optional prop, but fails when not present
+      <ButtonWithConcatenatedText />
+      <ButtonWithConcatenatedText
+        buttonUser={{
+          firstname: 'Norm',
+          lastname: 'Gunderson',
+          isLoggedIn: true,
+          loggedInColor: 'blue',
+          loggedOutColor: 'red',
+          age: -1,
+        }}
+       />
+      <ButtonWithConditionalState
+        buttonUser={{
+          firstname: 'Disabled',
+          lastname: 'Conditional Button',
+          isLoggedIn: false,
+          loggedInColor: 'blue',
+          loggedOutColor: 'red',
+          age: -1,
+        }}
+       />
+      <ButtonWithConditionalState
+        buttonUser={{
+          firstname: 'May Vote',
+          lastname: 'Conditional Button',
+          age: 19,
+          isLoggedIn: true,
+          loggedInColor: 'blue',
+          loggedOutColor: 'red',
+        }}
+      />
+      <ButtonWithConditionalState
+        buttonUser={{
+          firstname: 'May Not Vote',
+          lastname: 'Conditional Button',
+          age: 16,
+          isLoggedIn: true,
+          loggedInColor: 'blue',
+          loggedOutColor: 'red',
+        }}
+      />
+      */}
     </>
   );
 }
diff --git a/packages/test-generator/test-app-templates/src/models/User.ts b/packages/test-generator/test-app-templates/src/models/User.ts
new file mode 100644
index 000000000..21ddabb08
--- /dev/null
+++ b/packages/test-generator/test-app-templates/src/models/User.ts
@@ -0,0 +1,8 @@
+export type User = {
+  firstname: string;
+  lastname: string;
+  isLoggedIn: boolean;
+  age: number;
+  loggedInColor: string;
+  loggedOutColor: string;
+};
diff --git a/packages/test-generator/test-app-templates/src/models/index.ts b/packages/test-generator/test-app-templates/src/models/index.ts
new file mode 100644
index 000000000..f6b9f36c6
--- /dev/null
+++ b/packages/test-generator/test-app-templates/src/models/index.ts
@@ -0,0 +1 @@
+export * from './User';