Ticket Wizard

or the perils of ticket creation

by @gnz

Don't panic

It's the new wizard
Now 100% voodoo-free.

Features

The basic two are obvious:
  1. Ticket creation
  2. Ticket edition
  3. ..but many use cases

Vision

  • It should be a platform on top of which controls can be built.
  • It should provide services to the control, such as API to interact with other fields, prefilling & chaining, etc.
  • Implementing a new control types should be piece of cake.

How does it work?

  1. The structure is defined in XML in the creation pipe.
  2. The creation pipe is loaded into configuration classes.
  3. Using them, the wizard controller (UI) instantiates composite controls that recursively instantiate their childs all the way up to the fields/controls.

Defining structure

The creation pipe
  • There's an XSD
    • Used by the check page to validate
    • Should be added in the designer on Save
  • There's a set of classes for loading
    • ...and saving too!

Configuration classes

BusinessInterfaces\TicketWizard\ConfigurationBusinessEntities\TicketWizard\Configuration
  • They are in charge of loading the creation pipe.
  • Split the work into logical parts (Page, Section, Control, etc)
  • Mapped 1-to-1 to the valid XML defined in XSD .
  • Bi-directional: can load and also SAVE it back.
  • Not used currently in the wizard designer, but should.
  • XCM hooks onto these ones to transport wizards.

Control implementations

  • Controls types are MEF plug-ins
  • Implement  IWizardField
  • Inherit from  WizardField
  • Implemented interfaces define capabilities:
    • IChainableControl, IChangeHandler, IChangeNotifierControl, ISaveableControl, INameBoundPrefillProvider .

    Control implementations

    Currently still located on TxpEnterprise but should be moved into its own project.

    Composed my MEF.
    See?
    [WizardFieldDescriptor("CheckBox", typeof(WizardCheckBox))]
    public class WizardCheckBox : WizardField { }

    The FAQing control

    • How do I add a new property to a control?
      Simply create a C# property and attribute it with [WizardFieldProperty] 
    • Need to do something special on save? 
      Implement ISaveableControl
    • Need to implement some common prefilling? 
      Override the OnPreFill() method and access the Items[0] 
    • Need to implement a list-like control?
      Inherit from WizardListControl base class.
    • Need to implement prefills "a la DB query"?
      Implement INameBoundPrefillProvider (more to come...)
    • Need special styling on your control?
      The wizard adds quite some CSS classes already!

    Prefill & chaining

    Some definitions:
    • Chaining is when one control has a parent control, which, on update cascades the update to the child.
    • Prefilling is an overloaded term:
    1. Provide a default static value before creation, e.g. "client computer name". Easy case, handled automatically by the ticket wizard manager in the creation pipe level.
    2. Propagation of a field's value (or related values) into other fields. Hard case.

    PREFILL & CHAINING

    Think about them in terms of direction: 
    • Chaining = child points to parent. E.g. CI List control.
    • Prefilling = parent provides results and children receive them. E.g. DB Query control, Web service controls.

    To solve the hard case, we have infrastructure now:
    • Implement INameBoundPrefillProvider
    • It takes care of most of the propagation issues
    • First implemented in the new chainable DB controls

    Client side wizard

    • The wizard has a client-side API.
      • Really, it does!
    • Defined in WizardController.js
      • ...and populated with data emitted on WizardPage.cs
    • Provides a unified API surface to access and set all fields, and more:
      TicketWizard.Fields[];
      TicketWizard.Pages[];
      TicketWizard.Validate*();
      //etc. 

    Testing the wizard

    Everyone said it couldn't be done!
    • Based on CasperJS & PhantomJS
    • Isolates the wizard:
      • ...just enough to be able to inject configuration
      • ...but leave pieces that work (i.e. don't mock the world)
    • Relies on the "static manager + ChangeImplementation()" approach by HKO for isolation

      Testing the wizard

      • What is in a test?
        • A creation pipe file and .
        • (+ some additional configuration, rights, etc) 
      • How to kick in one individual test?
        • Copy the testable assembly to BIN
        • Pass the absolute path of the test creation pipe via query string
      • How to write tests?
        • Write a test creation pipe
        • Write some simple JS code

      TESTING THE WIZARD

      • We created the concept of wizard strategy  
        • The default strategy does the expected.
        • But there's a testable strategy that allows testing.
          • Needs to be copied manually into \bin, then run:
          • Isonet.TxpEnterprise.WebUiAutomation\run.bat 
      • The testable strategy does a few things:
        • Makes sure managers are replaced.
        • Renders a few special Javascript in the wizard to allow testing for the results of saving.
      • Currently only works for creation, not edit test cases!

        Here be dragons

        • There's still some legacy code around:
          • LogicalCreationPipe? Crap
          • Any direct XML manipulation? Crap
          • Checking the field type directly? Crap
        • Code duplication: some, but is not that bad.
        • Chaining and prefilling are TWO different things!
        • Chaining is always complex: many use cases!
        • SetDefaultValue feature? Not finished, not tested.

        The future

        • Controls should be moved into a separate project: 
          • True MEF composition
          • No references
          • No type checking cheats
        • Keep working on the testing infrastructure
        • Clean up the web service control implementations
        Made with Slides.com