text:
layout: page
title: Ryan Light Logistics Show
date: 2016-05-24
author: Jane Hurley
tags: weekly links, java
status: published
summary: Maecenas posuere iaculis arcu sed convallis.
banner: images/banner/meeting-01.jpg
booking:
  startDate: 04/06/2016
  endDate: 04/09/2016
  ctyhocn: TULBAHX
  groupCode: RLLS
published: true
---
Pellentesque ultricies semper porttitor. Ut ultricies, nisi id lacinia congue, leo neque tincidunt lacus, et feugiat magna neque sit amet ligula. Vivamus euismod euismod dui quis eleifend. Fusce a bibendum quam. Nunc iaculis id sapien et pharetra. Aenean posuere, est malesuada pulvinar condimentum, sem massa facilisis neque, ut elementum dui dui sit amet dolor. Donec et aliquet nisi. Praesent mollis elit ligula, id ullamcorper dolor imperdiet et. Aenean sed laoreet neque. Fusce efficitur a orci vel posuere. Ut malesuada elit vel semper facilisis. Sed eget sem in eros faucibus sagittis. Curabitur et dui in risus fringilla mollis. Praesent vestibulum enim sit amet hendrerit molestie. Ut efficitur, libero in ullamcorper pretium, arcu turpis viverra sem, id consequat mi ex et nulla. Proin in dolor hendrerit, ultricies ex sed, molestie lorem.

* Maecenas nec neque sit amet nulla venenatis rutrum vel sit amet neque
* Sed luctus metus bibendum odio imperdiet, vel consectetur dolor egestas
* Cras at nibh vitae neque elementum egestas
* Vivamus pellentesque quam bibendum enim placerat, ac placerat velit bibendum.

Suspendisse pellentesque lorem arcu, ut pharetra odio feugiat quis. Nulla facilisi. Maecenas ut metus risus. Duis sollicitudin risus et diam egestas tincidunt. Donec nibh nibh, ornare quis interdum ut, vestibulum et massa. Sed velit erat, tincidunt id eros ac, porta fermentum ex. Vivamus tempus ullamcorper lorem, id vulputate urna suscipit ultricies. Integer lectus eros, consectetur quis dui dictum, facilisis varius est. Nulla molestie felis sit amet dolor lobortis pellentesque. Cras finibus elementum sodales. Morbi quis risus ut massa varius ornare vitae et turpis.


text:
<meta http-equiv="content-type" content="text/html;charset=UTF-8" />

<META HTTP-EQUIV="Content-Type" CONTENT="text/html;charset=UTF-8"><META HTTP-EQUIV="Refresh" CONTENT="0; URL=2003/10/07/cingular-please-attend-to-your-own-customers/index.html"><TITLE>Page has moved</TITLE>

Click here...

redpajama_set_name: RedPajamaGithub


text:
var banner = require('./banner')

var uncc = module.exports = banner.submodule('uncc')
  .fullName('UNC Charlotte')
  .location('Charlotte NC 28202')
  .timezone('America/New_York')
  .uses('banner')
    .configure({
        seperateDepartments: true
      , seatsListedWithSections: false
      , sectionDetailsOnCrawl: false
      , dummy: true
    })
  .rootUrl('selfservice.uncc.edu')
  .pagePaths({
        termList: "/pls/BANPROD/bwckschd.p_disp_dyn_sched"
      , term: "/pls/BANPROD/bwckgens.p_proc_term_date"
      , listing: "/pls/BANPROD/bwckschd.p_get_crse_unsec"
      , details: "/pls/BANPROD/bwckschd.p_disp_detail_sched"
  })
  .debug(true)


text:
package diff

import (
	"bytes"
	"io"
	"reflect"
	"sort"

	"github.com/deckarep/golang-set"
	"github.com/schemalex/schemalex"
	"github.com/schemalex/schemalex/format"
	"github.com/schemalex/schemalex/internal/errors"
	"github.com/schemalex/schemalex/model"
)

type diffCtx struct {
	fromSet mapset.Set
	toSet   mapset.Set
	from    model.Stmts
	to      model.Stmts
}

func newDiffCtx(from, to model.Stmts) *diffCtx {
	fromSet := mapset.NewSet()
	for _, stmt := range from {
		if cs, ok := stmt.(model.Table); ok {
			fromSet.Add(cs.ID())
		}
	}
	toSet := mapset.NewSet()
	for _, stmt := range to {
		if cs, ok := stmt.(model.Table); ok {
			toSet.Add(cs.ID())
		}
	}

	return &diffCtx{
		fromSet: fromSet,
		toSet:   toSet,
		from:    from,
		to:      to,
	}
}

// Statements compares two model.Stmts and generates a series
// of statements to migrate from the old one to the new one,
// writing the result to `dst`
func Statements(dst io.Writer, from, to model.Stmts, options ...Option) error {
	var txn bool
	for _, o := range options {
		switch o.Name() {
		case optkeyTransaction:
			txn = o.Value().(bool)
		}
	}

	ctx := newDiffCtx(from, to)

	var procs = []func(*diffCtx, io.Writer) (int64, error){
		dropTables,
		createTables,
		alterTables,
	}

	var buf bytes.Buffer
	if txn {
		buf.WriteString("\nBEGIN;\n\nSET FOREIGN_KEY_CHECKS = 0;")
	}

	for _, p := range procs {
		var pbuf bytes.Buffer
		n, err := p(ctx, &pbuf)
		if err != nil {
			return errors.Wrap(err, `failed to produce diff`)
		}
		if txn && n > 0 || !txn && buf.Len() > 0 && n > 0 {
			buf.WriteString("\n\n")
		}
		pbuf.WriteTo(&buf)
	}
	if txn {
		buf.WriteString("\n\nSET FOREIGN_KEY_CHECKS = 1;\n\nCOMMIT;")
	}

	if _, err := buf.WriteTo(dst); err != nil {
		return errors.Wrap(err, `failed to write diff`)
	}
	return nil
}

// Strings compares two strings and generates a series
// of statements to migrate from the old one to the new one,
// writing the result to `dst`
func Strings(dst io.Writer, from, to string, options ...Option) error {
	var p *schemalex.Parser
	for _, o := range options {
		switch o.Name() {
		case optkeyParser:
			p = o.Value().(*schemalex.Parser)
		}
	}
	if p == nil {
		p = schemalex.New()
	}

	stmts1, err := p.ParseString(from)
	if err != nil {
		return errors.Wrapf(err, `failed to parse "from" %s`, from)
	}

	stmts2, err := p.ParseString(to)
	if err != nil {
		return errors.Wrapf(err, `failed to parse "to" %s`, to)
	}

	return Statements(dst, stmts1, stmts2, options...)
}

// Files compares contents of two files and generates a series
// of statements to migrate from the old one to the new one,
// writing the result to `dst`
func Files(dst io.Writer, from, to string, options ...Option) error {
	return Sources(dst, schemalex.NewLocalFileSource(from), schemalex.NewLocalFileSource(to), options...)
}

// Files compares contents from two sources and generates a series
// of statements to migrate from the old one to the new one,
// writing the result to `dst`
func Sources(dst io.Writer, from, to schemalex.SchemaSource, options ...Option) error {
	var buf bytes.Buffer
	if err := from.WriteSchema(&buf); err != nil {
		return errors.Wrapf(err, `failed to retrieve schema from "from" source %s`, from)
	}
	fromStr := buf.String()
	buf.Reset()

	if err := to.WriteSchema(&buf); err != nil {
		return errors.Wrapf(err, `failed to retrieve schema from "to" source %s`, to)
	}
	return Strings(dst, fromStr, buf.String(), options...)
}

func dropTables(ctx *diffCtx, dst io.Writer) (int64, error) {
	var buf bytes.Buffer
	ids := ctx.fromSet.Difference(ctx.toSet)
	for i, id := range ids.ToSlice() {
		if i > 0 {
			buf.WriteByte('\n')
		}

		stmt, ok := ctx.from.Lookup(id.(string))
		if !ok {
			return 0, errors.Errorf(`failed to lookup table %s`, id)
		}

		table, ok := stmt.(model.Table)
		if !ok {
			return 0, errors.Errorf(`lookup failed: %s is not a model.Table`, id)
		}
		buf.WriteString("DROP TABLE `")
		buf.WriteString(table.Name())
		buf.WriteString("`;")
	}

	return buf.WriteTo(dst)
}

func createTables(ctx *diffCtx, dst io.Writer) (int64, error) {
	var buf bytes.Buffer

	ids := ctx.toSet.Difference(ctx.fromSet)
	for _, id := range ids.ToSlice() {
		// Lookup the corresponding statement, and add its SQL
		stmt, ok := ctx.to.Lookup(id.(string))
		if !ok {
			return 0, errors.Errorf(`failed to lookup table %s`, id)
		}

		if buf.Len() > 0 {
			buf.WriteByte('\n')
		}

		if err := format.SQL(&buf, stmt); err != nil {
			return 0, err
		}
		buf.WriteByte(';')
	}
	return buf.WriteTo(dst)
}

type alterCtx struct {
	fromColumns mapset.Set
	toColumns   mapset.Set
	fromIndexes mapset.Set
	toIndexes   mapset.Set
	from        model.Table
	to          model.Table
}

func newAlterCtx(from, to model.Table) *alterCtx {
	fromColumns := mapset.NewSet()
	for col := range from.Columns() {
		fromColumns.Add(col.ID())
	}

	toColumns := mapset.NewSet()
	for col := range to.Columns() {
		toColumns.Add(col.ID())
	}

	fromIndexes := mapset.NewSet()
	for idx := range from.Indexes() {
		fromIndexes.Add(idx.ID())
	}

	toIndexes := mapset.NewSet()
	for idx := range to.Indexes() {
		toIndexes.Add(idx.ID())
	}

	return &alterCtx{
		fromColumns: fromColumns,
		toColumns:   toColumns,
		fromIndexes: fromIndexes,
		toIndexes:   toIndexes,
		from:        from,
		to:          to,
	}
}

func alterTables(ctx *diffCtx, dst io.Writer) (int64, error) {
	procs := []func(*alterCtx, io.Writer) (int64, error){
		dropTableIndexes,
		dropTableColumns,
		addTableColumns,
		alterTableColumns,
		addTableIndexes,
	}

	ids := ctx.toSet.Intersect(ctx.fromSet)
	var buf bytes.Buffer
	for _, id := range ids.ToSlice() {
		var stmt model.Stmt
		var ok bool

		stmt, ok = ctx.from.Lookup(id.(string))
		if !ok {
			return 0, errors.Errorf(`table '%s' not found in old schema (alter table)`, id)
		}
		beforeStmt := stmt.(model.Table)

		stmt, ok = ctx.to.Lookup(id.(string))
		if !ok {
			return 0, errors.Errorf(`table '%s' not found in new schema (alter table)`, id)
		}
		afterStmt := stmt.(model.Table)

		var pbuf bytes.Buffer
		alterCtx := newAlterCtx(beforeStmt, afterStmt)
		for _, p := range procs {
			n, err := p(alterCtx, &pbuf)
			if err != nil {
				return 0, errors.Wrap(err, `failed to generate alter table`)
			}

			if buf.Len() > 0 && n > 0 {
				buf.WriteByte('\n')
			}
			pbuf.WriteTo(&buf)
		}
	}

	return buf.WriteTo(dst)
}

func dropTableColumns(ctx *alterCtx, dst io.Writer) (int64, error) {
	columnNames := ctx.fromColumns.Difference(ctx.toColumns)

	var buf bytes.Buffer
	for _, columnName := range columnNames.ToSlice() {
		if buf.Len() > 0 {
			buf.WriteByte('\n')
		}
		buf.WriteString("ALTER TABLE `")
		buf.WriteString(ctx.from.Name())
		buf.WriteString("` DROP COLUMN `")
		col, ok := ctx.from.LookupColumn(columnName.(string))
		if !ok {
			return 0, errors.Errorf(`failed to lookup column %s`, columnName)
		}

		buf.WriteString(col.Name())
		buf.WriteString("`;")
	}

	return buf.WriteTo(dst)
}

func addTableColumns(ctx *alterCtx, dst io.Writer) (int64, error) {
	var buf bytes.Buffer

	beforeToNext := make(map[string]string) // lookup next column
	nextToBefore := make(map[string]string) // lookup before column

	// In order to do this correctly, we need to create a graph so that
	// we always start adding with a column that has a either no before
	// columns, or one that already exists in the database
	var firstColumn model.TableColumn
	for _, v := range ctx.toColumns.Difference(ctx.fromColumns).ToSlice() {
		columnName := v.(string)
		// find the before-column for each.
		col, ok := ctx.to.LookupColumn(columnName)
		if !ok {
			return 0, errors.Errorf(`failed to lookup column %s`, columnName)
		}

		beforeCol, hasBeforeCol := ctx.to.LookupColumnBefore(col.ID())
		if !hasBeforeCol {
			// if there is no before-column, then this is a special "FIRST"
			// column
			firstColumn = col
			continue
		}

		// otherwise, keep a reverse-lookup map of before -> next columns
		beforeToNext[beforeCol.ID()] = columnName
		nextToBefore[columnName] = beforeCol.ID()
	}

	// First column is always safe to add
	if firstColumn != nil {
		writeAddColumn(ctx, &buf, firstColumn.ID())
	}

	var columnNames []string
	// Find columns that have before columns which existed in both
	// from and to tables
	for _, v := range ctx.toColumns.Intersect(ctx.fromColumns).ToSlice() {
		columnName := v.(string)
		if nextColumnName, ok := beforeToNext[columnName]; ok {
			delete(beforeToNext, columnName)
			delete(nextToBefore, nextColumnName)
			columnNames = append(columnNames, nextColumnName)
		}
	}

	if len(columnNames) > 0 {
		sort.Strings(columnNames)
		writeAddColumn(ctx, &buf, columnNames...)
	}

	// Finally, we process the remaining columns.
	// All remaining columns are new, and they will depend on a
	// newly created column. This means we have to make sure to
	// create them in the order that they are dependent on.
	columnNames = columnNames[:0]
	for _, nextCol := range beforeToNext {
		columnNames = append(columnNames, nextCol)
	}
	// if there's one left, that can be appended
	if len(columnNames) > 0 {
		sort.Slice(columnNames, func(i, j int) bool {
			icol, _ := ctx.to.LookupColumnOrder(columnNames[i])
			jcol, _ := ctx.to.LookupColumnOrder(columnNames[j])
			return icol < jcol
		})
		writeAddColumn(ctx, &buf, columnNames...)
	}
	return buf.WriteTo(dst)
}

func writeAddColumn(ctx *alterCtx, buf *bytes.Buffer, columnNames ...string) error {
	for _, columnName := range columnNames {
		stmt, ok := ctx.to.LookupColumn(columnName)
		if !ok {
			return errors.Errorf(`failed to lookup column %s`, columnName)
		}

		beforeCol, hasBeforeCol := ctx.to.LookupColumnBefore(stmt.ID())
		if buf.Len() > 0 {
			buf.WriteByte('\n')
		}
		buf.WriteString("ALTER TABLE `")
		buf.WriteString(ctx.from.Name())
		buf.WriteString("` ADD COLUMN ")
		if err := format.SQL(buf, stmt); err != nil {
			return err
		}
		if hasBeforeCol {
			buf.WriteString(" AFTER `")
			buf.WriteString(beforeCol.Name())
			buf.WriteString("`")
		} else {
			buf.WriteString(" FIRST")
		}

		buf.WriteByte(';')
	}
	return nil
}

func alterTableColumns(ctx *alterCtx, dst io.Writer) (int64, error) {
	var buf bytes.Buffer
	columnNames := ctx.toColumns.Intersect(ctx.fromColumns)
	for _, columnName := range columnNames.ToSlice() {
		beforeColumnStmt, ok := ctx.from.LookupColumn(columnName.(string))
		if !ok {
			return 0, errors.Errorf(`column %s not found in old schema`, columnName)
		}

		afterColumnStmt, ok := ctx.to.LookupColumn(columnName.(string))
		if !ok {
			return 0, errors.Errorf(`column %s not found in new schema`, columnName)
		}

		if reflect.DeepEqual(beforeColumnStmt, afterColumnStmt) {
			continue
		}

		if buf.Len() > 0 {
			buf.WriteByte('\n')
		}
		buf.WriteString("ALTER TABLE `")
		buf.WriteString(ctx.from.Name())
		buf.WriteString("` CHANGE COLUMN `")
		buf.WriteString(afterColumnStmt.Name())
		buf.WriteString("` ")
		if err := format.SQL(&buf, afterColumnStmt); err != nil {
			return 0, err
		}
		buf.WriteByte(';')
	}

	return buf.WriteTo(dst)
}

func dropTableIndexes(ctx *alterCtx, dst io.Writer) (int64, error) {
	var buf bytes.Buffer
	indexes := ctx.fromIndexes.Difference(ctx.toIndexes)
	// drop index after drop constraint.
	// because cannot drop index if needed in a foreign key constraint
	lazy := make([]model.Index, 0, indexes.Cardinality())
	for _, index := range indexes.ToSlice() {
		indexStmt, ok := ctx.from.LookupIndex(index.(string))
		if !ok {
			return 0, errors.Errorf(`index '%s' not found in old schema (drop index)`, index)
		}

		if indexStmt.IsPrimaryKey() {
			if buf.Len() > 0 {
				buf.WriteByte('\n')
			}
			buf.WriteString("ALTER TABLE `")
			buf.WriteString(ctx.from.Name())
			buf.WriteString("` DROP PRIMARY KEY;")
			continue
		}

		if !indexStmt.HasName() && !indexStmt.HasSymbol() {
			return 0, errors.Errorf("can not drop index without name: %s", indexStmt.ID())
		}
		if !indexStmt.IsForeignKey() {
			lazy = append(lazy, indexStmt)
			continue
		}

		if buf.Len() > 0 {
			buf.WriteByte('\n')
		}
		buf.WriteString("ALTER TABLE `")
		buf.WriteString(ctx.from.Name())
		buf.WriteString("` DROP FOREIGN KEY `")
		if indexStmt.HasSymbol() {
			buf.WriteString(indexStmt.Symbol())
		} else {
			buf.WriteString(indexStmt.Name())
		}
		buf.WriteString("`;")
	}
	// drop index after drop CONSTRAINT
	for _, indexStmt := range lazy {
		if buf.Len() > 0 {
			buf.WriteByte('\n')
		}
		buf.WriteString("ALTER TABLE `")
		buf.WriteString(ctx.from.Name())
		buf.WriteString("` DROP INDEX `")
		if !indexStmt.HasName() {
			buf.WriteString(indexStmt.Symbol())
		} else {
			buf.WriteString(indexStmt.Name())
		}

		buf.WriteString("`;")
	}

	return buf.WriteTo(dst)
}

func addTableIndexes(ctx *alterCtx, dst io.Writer) (int64, error) {
	var buf bytes.Buffer
	indexes := ctx.toIndexes.Difference(ctx.fromIndexes)
	// add index before add foreign key.
	// because cannot add index if create implicitly index by foreign key.
	lazy := make([]model.Index, 0, indexes.Cardinality())
	for _, index := range indexes.ToSlice() {
		indexStmt, ok := ctx.to.LookupIndex(index.(string))
		if !ok {
			return 0, errors.Errorf(`index '%s' not found in old schema (add index)`, index)
		}
		if indexStmt.IsForeignKey() {
			lazy = append(lazy, indexStmt)
			continue
		}
		if buf.Len() > 0 {
			buf.WriteByte('\n')
		}
		buf.WriteString("ALTER TABLE `")
		buf.WriteString(ctx.from.Name())
		buf.WriteString("` ADD ")
		if err := format.SQL(&buf, indexStmt); err != nil {
			return 0, err
		}
		buf.WriteByte(';')
	}

	for _, indexStmt := range lazy {
		if buf.Len() > 0 {
			buf.WriteByte('\n')
		}
		buf.WriteString("ALTER TABLE `")
		buf.WriteString(ctx.from.Name())
		buf.WriteString("` ADD ")
		if err := format.SQL(&buf, indexStmt); err != nil {
			return 0, err
		}
		buf.WriteByte(';')
	}

	return buf.WriteTo(dst)
}


text:
package nanny

import (
	"fmt"
	"math"

	"k8s.io/kubernetes/pkg/api/resource"
	api "k8s.io/kubernetes/pkg/api/v1"

	log "github.com/golang/glog"
)

// Resource defines the name of a resource, the quantity, and the marginal value.
type Resource struct {
	Base, ExtraPerNode resource.Quantity
	Name               api.ResourceName
}

// ResourceListPair is a pair of ResourceLists, denoting a range.
type ResourceListPair struct {
	// lower bound of the resource range.
	lower api.ResourceList
	// upper bound of the resource range.
	upper api.ResourceList
}

// EstimatorResult is the result of the resource Estimation, used by Estimator struct.
type EstimatorResult struct {
	// Recommended range is used for setting new values of resource requirements.
	RecommendedRange ResourceListPair
	// Acceptable range specifies which requirements are acceptable and doesn't need to be changed.
	AcceptableRange ResourceListPair
}

// Estimator is a struct used for estimating accepted and recommended resource requirements.
type Estimator struct {
	// Specification of monitored resources.
	Resources []Resource
	// Percentage offset defining acceptable resource range.
	AcceptanceOffset int64
	// Percentage offset defining recommended resource range.
	RecommendationOffset int64
}

func decWithPercentageOffset(value uint64, offset int64, rounder func(float64) float64) uint64 {
	return uint64(int64(value) + int64(rounder(float64(offset)*float64(value)/100)))
}

func nodesAndOffsetToRange(numNodes uint64, offset int64, res []Resource) ResourceListPair {
	numNodesMin := decWithPercentageOffset(numNodes, -offset, math.Floor)
	numNodesMax := decWithPercentageOffset(numNodes, offset, math.Ceil)
	return ResourceListPair{
		lower: calculateResources(numNodesMin, res),
		upper: calculateResources(numNodesMax, res),
	}
}

func (e Estimator) scaleWithNodes(numNodes uint64) *EstimatorResult {
	return &EstimatorResult{
		RecommendedRange: nodesAndOffsetToRange(numNodes, e.RecommendationOffset, e.Resources),
		AcceptableRange:  nodesAndOffsetToRange(numNodes, e.AcceptanceOffset, e.Resources),
	}
}

func calculateResources(numNodes uint64, resources []Resource) api.ResourceList {
	resourceList := make(api.ResourceList)
	for _, r := range resources {
		// Since we want to enable passing values smaller than e.g. 1 millicore per node,
		// we need to have some more hacky solution here than operating on MilliValues.
		perNodeString := r.ExtraPerNode.String()
		var perNode float64
		read, _ := fmt.Sscanf(perNodeString, "%f", &perNode)
		overhead := resource.MustParse(fmt.Sprintf("%f%s", perNode*float64(numNodes), perNodeString[read:]))

		newRes := r.Base
		newRes.Add(overhead)

		log.V(4).Infof("New requirement for resource %s with %d nodes is %s", r.Name, numNodes, newRes.String())

		resourceList[r.Name] = newRes
	}
	return resourceList
}


text:
README
======

About Us
--------

https://jaydean.net

Requirements
------------

* PHP >=5.4.0
* Guzzle ~4.0

Installing via Composer
-----------------------

The recommended way to install is through
[Composer](http://getcomposer.org).

```bash
# Install Composer
curl -sS https://getcomposer.org/installer | php
```

Next, update your project's composer.json file:

```javascript
{
    "require": {
        "jaydean/claredale-sync": "dev-master"
    }
}
```

After installing, you need to require Composer's autoloader:

```php
require 'vendor/autoload.php';
```


text:
namespace LINQPadUtils.Fragments
{
    using System;
    using System.Collections;
    using System.Text;

    using LINQPadUtils.Markup;
    using LINQPadUtils.MetadataProviders;

    class PrimitiveEnumerableTableRowsFragment : FragmentBase
    {
        public PrimitiveEnumerableTableRowsFragment(TypeMetadataProviderBase metadata)
            : base(metadata)
        {
        }

        public override string Render(int depthLimit, int currentDepth)
        {
            var rowDataSb = new StringBuilder();

            if (base.Metadata.IsEnumerable && base.Metadata.IsPrimitiveElement)
            {
                // check the collection to see if it's all strongly typed, or all primitives so the columns can be built.
                foreach (var item in (IEnumerable)this.Metadata.SourceObject)
                {
                    rowDataSb.Append("<tr>");
                        //TODO: I don't think we need all this code if it's just going to render primitive types.

                    var renderedValue = ValueDisplay.GetDisplayValue(item);

                    var newTag = HtmlTag.WrapValue("td", renderedValue);

                    rowDataSb.Append(newTag);

                    rowDataSb.Append("</tr>");
                }
            }
            else
            {
                throw new InvalidOperationException(
                    "Cannot render rows for a type which does not implement IEnumerable<T>.");
            }

            return rowDataSb.ToString();
        }
    }
}


text:
import React from 'react';
import { assert } from 'chai';
import { createShallow, getClasses } from '../test-utils';
import DialogContentText from './DialogContentText';

describe('<DialogContentText />', () => {
  let shallow;
  let classes;

  before(() => {
    shallow = createShallow({ dive: true });
    classes = getClasses(<DialogContentText />);
  });

  describe('prop: className', () => {
    it('should render with the user and root classes', () => {
      const wrapper = shallow(<DialogContentText className="woofDialogContentText" />);
      assert.strictEqual(wrapper.hasClass('woofDialogContentText'), true);
      assert.strictEqual(wrapper.hasClass(classes.root), true);
    });
  });

  describe('prop: children', () => {
    it('should render children', () => {
      const children = <p />;
      const wrapper = shallow(
        <DialogContentText>
          {children}
        </DialogContentText>,
      );
      assert.strictEqual(wrapper.children().equals(children), true);
    });
  });
});


text:
<?php

namespace HXPHP\System;

class View {
	/**
	 * Título da página
	 * @var string
	 */
	public $title = null;

	/**
	 * Injeção das Configurações
	 * @var object
	 */
	private $configs;

	/**
	 * Injeção do Http Request
	 * @var object
	 */
	private $request;

	/**
	 * Parâmetros de configuração da VIEW
	 * @var string
	 */
	protected $partialsDir = null;
	protected $path        = null;
	protected $template    = null;
	protected $header      = null;
	protected $file        = null;
	protected $footer      = null;
	protected $vars        = [];
	protected $assets      = [
		'css' => [],
		'js'  => []
	];

	public function setConfigs(Configs\Config $configs, $subfolder, $controller, $action) {
		/**
		 * Injeção das Configurações
		 * @var object
		 */
		$this->configs = $configs;
		$this->request = new Http\Request($configs->baseURI);

		/**
		 * Tratamento das variáveis
		 */
		$controller = strtolower(str_replace('Controller', '', $controller));
		$action     = ($controller == $configs->controllers->notFound
			?'indexAction':$action);
		$action = str_replace('Action', '', $action);

		/**
		 * Verifica se os valores padrão foram alterados no construtor do Controller.
		 */
		$view_settings = new \stdClass;

		$default_values = [
			'partialsDir' => 'partials',
			'path'        => $subfolder.$controller,
			'template'    => true,
			'header'      => $subfolder.'header',
			'file'        => $action,
			'footer'      => $subfolder.'footer',
			'title'       => $this->configs->title
		];

		foreach ($default_values as $setting => $value) {
			if (!$this->$setting) {
				$view_settings->$setting = $value;
				continue;
			}

			$view_settings->$setting = $this->$setting;
		}

		$this->setPartialsDir($view_settings->partialsDir)
			->setPath($view_settings->path)
			->setTemplate($view_settings->template)
			->setHeader($view_settings->header)
			->setFile($view_settings->file)
			->setFooter($view_settings->footer)
			->setTitle($view_settings->title);
	}

	/**
	 * Define o diretório das views parciais
	 * @param string  $partialsDir  Diretório
	 */
	public function setPartialsDir($partialsDir) {
		$viewsDir = $this->configs->views->directory;

		$this->partialsDir = $viewsDir.DS.$partialsDir.DS;
		return $this;
	}

	/**
	 * Define o título da página
	 * @param string  $title  Título da página
	 */
	public function setTitle($title) {
		$this->title = $title;
		return $this;
	}

	/**
	 * Define a pasta da view
	 * @param string  $path  Caminho da View
	 */
	public function setPath($path) {
		$this->path = $path;
		return $this;
	}

	/**
	 * Define se o arquivo é miolo (Inclusão de Cabeçalho e Rodapé) ou único
	 * @param bool  $template  Template ON/OFF
	 */
	public function setTemplate($template) {
		$this->template = $template;
		return $this;
	}

	/**
	 * Define o cabeçalho da view
	 * @param string  $header  Cabeçalho da View
	 */
	public function setHeader($header) {
		$this->header = $header;
		return $this;
	}

	/**
	 * Define o arquivo da view
	 * @param string  $file  Arquivo da View
	 */
	public function setFile($file) {
		$this->file = $file;
		return $this;
	}

	/**
	 * Define o rodapé da view
	 * @param string  $footer  Rodapé da View
	 */
	public function setFooter($footer) {
		$this->footer = $footer;
		return $this;
	}

	/**
	 * Define um conjunto de variáveis para a VIEW
	 * @param array  $vars  Array com variáveis
	 */
	public function setVars(array $vars) {
		$this->vars = array_merge($this->vars, $vars);
		return $this;
	}

	/**
	 * Define uma variável única para a VIEW
	 * @param string  $name  Nome do índice
	 * @param string  $value  Valor
	 */
	public function setVar($name, $value) {
		$this->vars[$name] = $value;
		return $this;
	}

	/**
	 * Define os arquivos customizáveis que serão utilizados
	 * @param string  $type  Tipo do arquivo
	 * @param string|array  $assets  Arquivo Único | Array com os arquivos
	 */
	public function setAssets($type, $assets) {
		(is_array($assets))?
		$this->assets[$type] = array_merge($this->assets[$type], $assets):
		array_push($this->assets[$type], $assets);

		return $this;
	}

	/**
	 * Inclui os arquivos customizados
	 * @param  string $type          Tipo de arquivo incluso, como: css ou js
	 * @param  array  $custom_assets Links dos arquivos que serão incluídos
	 * @return string                HTML formatado de acordo com o tipo de arquivo
	 */

	private function assets($type, array $custom_assets = []) {
		$add_assets = '';

		switch ($type) {
			case 'css':
				$tag = '<link type="text/css" rel="stylesheet" href="%s">'."\n\r";
				break;

			case 'js':
				$tag = '<script type="text/javascript" src="%s"></script>'."\n\r";
				break;
		}

		if (count($custom_assets)) {
			foreach ($custom_assets as $file)
			$add_assets .= sprintf($tag, $file);
		}

		return $add_assets;
	}

	/**
	 * Renderiza a VIEW
	 * @param  string  $view  Nome do arquivo, sem extensão, a ser utilizado como VIEW
	 */
	public function flush() {

		$default_data = [
			'title' => $this->title
		];

		$data = array_merge($default_data, $this->vars);

		//Extract que transforma os parâmetros em variáveis disponíveis para a VIEW
		extract($data, EXTR_PREFIX_ALL, 'view');

		//Inclusão de ASSETS

		$add_css = $this->assets('css', $this->assets['css']);
		$add_js  = $this->assets('js', $this->assets['js']);

		//Variáveis
		$baseURI  = $this->configs->baseURI;
		$viewsDir = $this->configs->views->directory;
		$viewsExt = $this->configs->views->extension;

		//Atribuição das constantes
		define('BASE', $baseURI);
		define('ASSETS', $baseURI.'public/assets/');
		define('BOWER', $baseURI.'public/bower_components/');
		define('IMG', $baseURI.'public/img/');
		define('CSS', $baseURI.'public/css/');
		define('JS', $baseURI.'public/js/');

		//Verifica a existência da VIEW
		$view = $viewsDir.$this->path.DS.$this->file.$viewsExt;

		if (!file_exists($view)) {
			throw new \Exception("Erro fatal: A view <'$view'> não foi encontrada. Por favor, crie a view e tente novamente.", 1);
		}

		//Mecanismo de template
		if (!$this->template) {
			//Inclusão da view
			require_once ($view);
			exit();
		}

		//Verifica a existência do Header e Footer customizado
		$header = $viewsDir.$this->header.$viewsExt;
		$footer = $viewsDir.$this->footer.$viewsExt;

		if (!file_exists($header) || !file_exists($footer)) {
			throw new \Exception("Erro fatal: O header <$header> ou o footer <$footer> não existe. Por favor, verifique e tente novamente.");
		}

		//Inclusão dos arquivos
		require_once ($header);
		require_once ($view);
		require_once ($footer);

		exit();
	}

	public function partial($view, array $params = []) {
		if (!empty($params)) {
			extract($params, EXTR_PREFIX_ALL, 'view');
		}

		$viewsExt = $this->configs->views->extension;

		$viewFile = $this->partialsDir.'_'.$view.$viewsExt;

		if (!file_exists($viewFile)) {
			throw new \Exception("Erro fatal: A view <'$viewFile'> não foi encontrada. Por favor, crie a view e tente novamente.", 1);
		}

		require_once ($viewFile);
	}
}


text:
@interface TwoVC : UIViewController

@end


text:
using System;

#if STATIC
using MetaType = IKVM.Reflection.Type;
using IKVM.Reflection;
#else
using MetaType = System.Type;
using System.Reflection;
#endif

namespace Mono.CSharp {

	public class EnumMember : Const
	{
		class EnumTypeExpr : TypeExpr
		{
			public override TypeSpec ResolveAsType (IMemberContext ec)
			{
				type = ec.CurrentType;
				eclass = ExprClass.Type;
				return type;
			}
		}

		public EnumMember (Enum parent, MemberName name, Attributes attrs)
			: base (parent, new EnumTypeExpr (), Modifiers.PUBLIC, name, attrs)
		{
		}

		static bool IsValidEnumType (TypeSpec t)
		{
			switch (t.BuiltinType) {
			case BuiltinTypeSpec.Type.Int:
			case BuiltinTypeSpec.Type.UInt:
			case BuiltinTypeSpec.Type.Long:
			case BuiltinTypeSpec.Type.Byte:
			case BuiltinTypeSpec.Type.SByte:
			case BuiltinTypeSpec.Type.Short:
			case BuiltinTypeSpec.Type.UShort:
			case BuiltinTypeSpec.Type.ULong:
			case BuiltinTypeSpec.Type.Char:
				return true;
			default:
				return t.IsEnum;
			}
		}

		public override Constant ConvertInitializer (ResolveContext rc, Constant expr)
		{
			if (expr is EnumConstant)
				expr = ((EnumConstant) expr).Child;

			var underlying = ((Enum) Parent).UnderlyingType;
			if (expr != null) {
				expr = expr.ImplicitConversionRequired (rc, underlying);
				if (expr != null && !IsValidEnumType (expr.Type)) {
					Enum.Error_1008 (Location, Report);
					expr = null;
				}
			}

			if (expr == null)
				expr = New.Constantify (underlying, Location);

			return new EnumConstant (expr, MemberType);
		}

		public override bool Define ()
		{
			if (!ResolveMemberType ())
				return false;

			const FieldAttributes attr = FieldAttributes.Public | FieldAttributes.Static | FieldAttributes.Literal;
			FieldBuilder = Parent.TypeBuilder.DefineField (Name, MemberType.GetMetaInfo (), attr);
			spec = new ConstSpec (Parent.Definition, this, MemberType, FieldBuilder, ModFlags, initializer);

			Parent.MemberCache.AddMember (spec);
			return true;
		}

		public override void Accept (StructuralVisitor visitor)
		{
			visitor.Visit (this);
		}

	}

	/// <summary>
	///   Enumeration container
	/// </summary>
	public class Enum : TypeDefinition
	{
		//
		// Implicit enum member initializer, used when no constant value is provided
		//
		sealed class ImplicitInitializer : Expression
		{
			readonly EnumMember prev;
			readonly EnumMember current;

			public ImplicitInitializer (EnumMember current, EnumMember prev)
			{
				this.current = current;
				this.prev = prev;
			}

			public override bool ContainsEmitWithAwait ()
			{
				return false;
			}

			public override Expression CreateExpressionTree (ResolveContext ec)
			{
				throw new NotSupportedException ("Missing Resolve call");
			}

			protected override Expression DoResolve (ResolveContext rc)
			{
				// We are the first member
				if (prev == null) {
					return New.Constantify (current.Parent.Definition, Location);
				}

				var c = ((ConstSpec) prev.Spec).GetConstant (rc) as EnumConstant;
				try {
					return c.Increment ();
				} catch (OverflowException) {
					rc.Report.Error (543, current.Location,
						"The enumerator value `{0}' is outside the range of enumerator underlying type `{1}'",
						current.GetSignatureForError (), ((Enum) current.Parent).UnderlyingType.GetSignatureForError ());

					return New.Constantify (current.Parent.Definition, current.Location);
				}
			}

			public override void Emit (EmitContext ec)
			{
				throw new NotSupportedException ("Missing Resolve call");
			}
		}

		public static readonly string UnderlyingValueField = "value__";

		const Modifiers AllowedModifiers =
			Modifiers.NEW |
			Modifiers.PUBLIC |
			Modifiers.PROTECTED |
			Modifiers.INTERNAL |
			Modifiers.PRIVATE;

		readonly FullNamedExpression underlying_type_expr;

		public Enum (TypeContainer parent, FullNamedExpression type, Modifiers mod_flags, MemberName name, Attributes attrs)
			: base (parent, name, attrs, MemberKind.Enum)
		{
			underlying_type_expr = type;
			var accmods = IsTopLevel ? Modifiers.INTERNAL : Modifiers.PRIVATE;
			ModFlags = ModifiersExtensions.Check (AllowedModifiers, mod_flags, accmods, Location, Report);
			spec = new EnumSpec (null, this, null, null, ModFlags);
		}

		#region Properties

		public override AttributeTargets AttributeTargets {
			get {
				return AttributeTargets.Enum;
			}
		}

		public FullNamedExpression BaseTypeExpression {
			get {
				return underlying_type_expr;
			}
		}

		protected override TypeAttributes TypeAttr {
			get {
				return base.TypeAttr | TypeAttributes.Class | TypeAttributes.Sealed;
			}
		}

		public TypeSpec UnderlyingType {
			get {
				return ((EnumSpec) spec).UnderlyingType;
			}
		}

		#endregion

		public override void Accept (StructuralVisitor visitor)
		{
			visitor.Visit (this);
		}

		public void AddEnumMember (EnumMember em)
		{
			if (em.Name == UnderlyingValueField) {
				Report.Error (76, em.Location, "An item in an enumeration cannot have an identifier `{0}'",
					UnderlyingValueField);
				return;
			}

			AddMember (em);
		}

		public static void Error_1008 (Location loc, Report Report)
		{
			Report.Error (1008, loc,
				"Type byte, sbyte, short, ushort, int, uint, long or ulong expected");
		}

		protected override void DoDefineContainer ()
		{
			((EnumSpec) spec).UnderlyingType = underlying_type_expr == null ? Compiler.BuiltinTypes.Int : underlying_type_expr.Type;

			TypeBuilder.DefineField (UnderlyingValueField, UnderlyingType.GetMetaInfo (),
				FieldAttributes.Public | FieldAttributes.SpecialName | FieldAttributes.RTSpecialName);

			DefineBaseTypes ();
		}

		protected override bool DoDefineMembers ()
		{
			for (int i = 0; i < Members.Count; ++i) {
				EnumMember em = (EnumMember) Members[i];
				if (em.Initializer == null) {
					em.Initializer = new ImplicitInitializer (em, i == 0 ? null : (EnumMember) Members[i - 1]);
				}

				em.Define ();
			}

			return true;
		}

		public override bool IsUnmanagedType ()
		{
			return true;
		}

		protected override TypeSpec[] ResolveBaseTypes (out FullNamedExpression base_class)
		{
			base_type = Compiler.BuiltinTypes.Enum;
			base_class = null;
			return null;
		}

		protected override bool VerifyClsCompliance ()
		{
			if (!base.VerifyClsCompliance ())
				return false;

			switch (UnderlyingType.BuiltinType) {
			case BuiltinTypeSpec.Type.UInt:
			case BuiltinTypeSpec.Type.ULong:
			case BuiltinTypeSpec.Type.UShort:
				Report.Warning (3009, 1, Location, "`{0}': base type `{1}' is not CLS-compliant",
					GetSignatureForError (), UnderlyingType.GetSignatureForError ());
				break;
			}

			return true;
		}
	}

	class EnumSpec : TypeSpec
	{
		TypeSpec underlying;

		public EnumSpec (TypeSpec declaringType, ITypeDefinition definition, TypeSpec underlyingType, MetaType info, Modifiers modifiers)
			: base (MemberKind.Enum, declaringType, definition, info, modifiers | Modifiers.SEALED)
		{
			this.underlying = underlyingType;
		}

		public TypeSpec UnderlyingType {
			get {
				return underlying;
			}
			set {
				if (underlying != null)
					throw new InternalErrorException ("UnderlyingType reset");

				underlying = value;
			}
		}

		public static TypeSpec GetUnderlyingType (TypeSpec t)
		{
			return ((EnumSpec) t.GetDefinition ()).UnderlyingType;
		}

		public static bool IsValidUnderlyingType (TypeSpec type)
		{
			switch (type.BuiltinType) {
			case BuiltinTypeSpec.Type.Int:
			case BuiltinTypeSpec.Type.UInt:
			case BuiltinTypeSpec.Type.Long:
			case BuiltinTypeSpec.Type.Byte:
			case BuiltinTypeSpec.Type.SByte:
			case BuiltinTypeSpec.Type.Short:
			case BuiltinTypeSpec.Type.UShort:
			case BuiltinTypeSpec.Type.ULong:
				return true;
			}

			return false;
		}
	}
}


text:
Congratulations! Your purchase has been completed!

Please follow this instructions to get your order:

{{ instructions|safe }}


text:
<?php

// If uninstall, not called from WordPress, then exit
if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) {
	exit;
}

// Includes
require_once( plugin_dir_path( __FILE__ ) . 'class-post-type-ensenanzas.php' );

// Fire desactivate funtion and uninstall function
register_deactivation_hook( __FILE__, array( 'Post_Type_Ensenanzas', 'deactivate' ) );
register_uninstall_hook( __FILE__, array( 'Post_Type_Ensenanzas', 'uninstall' ) );


text:
<?xml version="1.0" encoding="utf-8"?>
<AbsoluteLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="@drawable/background_screen_intro"
    android:id="@+id/intro_background">
    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_x="0dp"
        android:layout_y="0dp"
        android:orientation="vertical"
        android:gravity="bottom">
        <ImageView
            android:id="@+id/start_button"
            android:src="@drawable/button_start_off"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:clickable="true" />
        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="5dip"
            android:id="@+id/linearLayout1" />
        <ImageView
            android:id="@+id/option_button"
            android:src="@drawable/button_options_off"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:clickable="true" />
        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="35dip" />
    </LinearLayout>
</AbsoluteLayout>


text:
package adullact.publicrowdfunding.model.server.entities;

/**
 * @author Ferrand and Nelaupe
 */
public class ServerProject {
    public int id;
    public int active;
    public String proposedBy;
    public int validate;
    public String name;
    public String description;
    public String currentFunding;
    public String requestedFunding;
    public String creationDate;
    public String beginDate;
    public String endDate;
    public double latitude;
    public double longitude;
    public int illustration;
    public String email;
    public String website;
    public String phone;
}


text:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<title>mobi.pocketmedia.nativeadslib.core.networking (Pocket Media Native Ads Library - Library to ease the integration with Pocket Media Native Ads platform)</title>
<meta name="date" content="2017-02-24">
<link rel="stylesheet" type="text/css" href="../../../../../stylesheet.css" title="Style">

JavaScript is disabled on your browser.

Skip navigation links

Overview

Package

Class

Tree

Deprecated

Index

Help

Prev Package

Next Package

Frames

No Frames

All Classes

Package mobi.pocketmedia.nativeadslib.core.networking

 Provides the classes in charge of retrieving ads and notifying the request listeners when it's done

See: Description

Interface Summary

Interface
Description

NativeAdsRequestListener

Callback listener for the native ad request.

Class Summary

Class
Description

NativeAdAsyncRequestTask

Created by iain17 on 26/10/2016.

NativeAdRequest

This class is used to perform a network request and retrieve a number of ads.

Package mobi.pocketmedia.nativeadslib.core.networking Description

 Provides the classes in charge of retrieving ads and notifying the request listeners when it's done

Skip navigation links

Overview

Package

Class

Tree

Deprecated

Index

Help

Prev Package

Next Package

Frames

No Frames

All Classes


text:
package com.qingbo.ginkgo.ygb.trade.repository;

import javax.annotation.Resource;

import org.junit.Test;

import com.qingbo.ginkgo.ygb.common.util.QueuingMake;
import com.qingbo.ginkgo.ygb.trade.entity.Deal;
import com.qingbo.ginkgo.ygb.trade.entity.Trade;
import com.qingbo.ginkgo.ygb.trade.entity.TradeLog;
import com.qingbo.ginkgo.ygb.trade.entity.TradeSchedule;

public class TradeRepositoryTester extends BaseRepositoryTester {
	@Resource private TradeRepository trade;
	@Resource private TradeLogRepository tradeLog;
	@Resource private TradeScheduleRepository tradeSchedule;
	@Resource private DealRepository deal;

	@Test
	public void fineOne() {
		Trade tr = new Trade();
		tr.setId(QueuingMake.next("01"));
		tr= trade.save(tr);
		System.out.println("Trade "+tr.getBatchId());
		tr = trade.findOne(tr.getId());
		tr.setBatchId(QueuingMake.next("01"));
		tr.setSourceTradeId(QueuingMake.next("01"));
		tr.setCreditAccount("TestAcccount");
		trade.save(tr);
		System.out.println("Trade "+tr.getCreditAccount());

		TradeLog trLog = new TradeLog();
		trLog.setId(QueuingMake.next("01"));
		trLog.setTradeId(tr.getId());
		trLog = tradeLog.save(trLog);
		System.out.println("TradeLog "+trLog.getCreateAt());

		TradeSchedule trSche = new TradeSchedule();
		trSche.setId(QueuingMake.next("01"));
		trSche.setScheduleInfo("test info");
		trSche = tradeSchedule.save(trSche);
		System.out.println("TradeSchedule "+trSche.getCreateAt());
		trSche = tradeSchedule.findOne(trSche.getId());
		System.out.println("TradeSchedule "+trSche.getCreateAt());

		Deal d = new Deal();
		d.setId(QueuingMake.next("01"));
		d = deal.save(d);
		System.out.println("Deal "+d.getCreateAt());
		d = deal.findOne(d.getId());
		System.out.println("Deal "+d.getCreateAt());

	}

}


text:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
   <solid android:color="#00ff00ff" />
    <corners  android:bottomLeftRadius="0dp"
        android:bottomRightRadius="15dp"
        android:topLeftRadius="0dp"
        android:topRightRadius="15dp"/>

        <stroke
        android:width="1dp"
        android:color="@color/white" />

</shape>


text:
import expect from 'expect-legacy';
import { createLogic, createLogicMiddleware } from '../src/index';

describe('createLogicMiddleware', () => {
  describe('createLogicMiddleware()', () => {
    let mw;

    beforeEach(() => {
      mw = createLogicMiddleware();
    });

    it('returns a mw fn with addLogic and replaceLogic props', () => {
      expect(mw).toBeA(Function);
      expect(mw.addLogic).toBeA(Function);
      expect(mw.replaceLogic).toBeA(Function);
    });

    it('returns a mw fn with an addDeps method', () => {
      expect(mw.addDeps).toBeA(Function);
    });

    it('works as middleware', () => {
      const next = expect.createSpy();
      const action = { type: 'FOO' };
      const result = mw({})(next)(action);
      expect(next.calls.length).toBe(1);
      expect(next.calls[0].arguments[0]).toEqual(action);
      expect(result).toBe(action);
    });

    it('mw.monitor$ to monitor flow', () => {
      const monArr = [];
      mw.monitor$.subscribe((x) => monArr.push(x));
      const next = expect.createSpy();
      const action = { type: 'FOO' };
      mw({})(next)(action);
      expect(monArr).toEqual([
        { action: { type: 'FOO' }, op: 'top' },
        { nextAction: { type: 'FOO' }, op: 'bottom' },
      ]);
    });

    it('mw.whenComplete(fn) should be called when complete', (bDone) => {
      mw.whenComplete(bDone);
    });

    it('mw.whenComplete(fn) should resolve to promise', (bDone) => {
      function fn() {}
      mw.whenComplete(fn).then(bDone);
    });

    it('mw.whenComplete() should resolve to promise', (bDone) => {
      mw.whenComplete().then(bDone);
    });

    it('assigning a same store to mw instance is ok', () => {
      const store = {};
      mw(store); // assign to store
      mw(store); // assign to same store is ok
    });

    it('assigning a mw instance to multple stores, throws', () => {
      const assignToMultipleStores = () => {
        mw({}); // assign to first store
        mw({}); // assign to a different store
      };
      expect(assignToMultipleStores).toThrow('multiple stores');
    });
  });

  describe('createLogicMiddleware(nonArray)', () => {
    it('throws an error', () => {
      expect(() => {
        createLogicMiddleware({});
      }).toThrow('called with an array');
    });
  });

  describe('createLogicMiddleware(duplicateArray)', () => {
    it('throws an error', () => {
      const fooLogic = createLogic({ type: 'FOO' });
      const barLogic = createLogic({ type: 'BAR' });
      const arrLogic = [fooLogic, barLogic, fooLogic];
      expect(() => {
        createLogicMiddleware(arrLogic);
      }).toThrow('duplicate logic');
    });
  });

  describe('createLogicMiddleware(duplicateArray2)', () => {
    it('throws an error', () => {
      const fooLogic = createLogic({ type: 'FOO' });
      const barLogic = createLogic({ type: 'BAR' });
      const arrLogic = [fooLogic, barLogic, fooLogic, barLogic];
      expect(() => {
        createLogicMiddleware(arrLogic);
      }).toThrow('duplicate logic');
    });
  });

  describe('createLogicMiddleware(similarArray)', () => {
    it('is allowed', () => {
      const fooLogic = createLogic({ type: 'FOO' });
      const foo2Logic = createLogic({ type: 'FOO' });
      const arrLogic = [fooLogic, foo2Logic];
      createLogicMiddleware(arrLogic);
    });
  });

  describe('[logicA] no dispatch', () => {
    let monArr = [];
    let mw;
    let logicA;
    let next;
    let dispatch;
    beforeEach(() => {
      monArr = [];
      next = expect.createSpy();
      dispatch = expect.createSpy();
      logicA = createLogic({
        type: 'FOO',
      });
      mw = createLogicMiddleware([logicA]);
      mw.monitor$.subscribe((x) => monArr.push(x));
      mw({ dispatch })(next);
    });

    it('mw.whenComplete(fn) should be called', (bDone) => {
      mw.whenComplete(bDone);
    });
  });

  describe('[logicA] non-matching dispatch', () => {
    let monArr = [];
    let mw;
    let logicA;
    let next;
    let dispatch;
    beforeEach(() => {
      monArr = [];
      next = expect.createSpy();
      dispatch = expect.createSpy();
      logicA = createLogic({
        type: 'FOO',
      });
      mw = createLogicMiddleware([logicA]);
      mw.monitor$.subscribe((x) => monArr.push(x));
      const storeFn = mw({ dispatch })(next);
      storeFn({ type: 'BAR' });
    });

    it('mw.whenComplete(fn) should be called', (bDone) => {
      mw.whenComplete(bDone);
    });
  });

  describe('[logicA] non-matching dispatches', () => {
    let monArr = [];
    let mw;
    let logicA;
    let next;
    let dispatch;
    beforeEach(() => {
      monArr = [];
      next = expect.createSpy();
      dispatch = expect.createSpy();
      logicA = createLogic({
        type: 'FOO',
      });
      mw = createLogicMiddleware([logicA]);
      mw.monitor$.subscribe((x) => monArr.push(x));
      const storeFn = mw({ dispatch })(next);
      storeFn({ type: 'BAR' });
      storeFn({ type: 'BAZ' });
    });

    it('mw.whenComplete(fn) should be called', (bDone) => {
      mw.whenComplete(bDone);
    });
  });

  describe('[logicA] type is string, match only', () => {
    let monArr = [];
    let mw;
    let logicA;
    let next;
    let dispatch;
    const actionA = { type: 'FOO' };
    const actionAResult = { type: 'FOO', allowed: ['a'] };
    const actionADispatch = { type: 'BAR', allowed: ['a'] };
    const actionIgnore = { type: 'CAT' };
    beforeEach((bDone) => {
      monArr = [];
      next = expect.createSpy();
      dispatch = expect.createSpy();
      logicA = createLogic({
        type: 'FOO',
        validate({ action }, allow) {
          allow({
            ...action,
            allowed: ['a'],
          });
        },
        process({ action }, dispatch) {
          dispatch({
            ...action,
            type: 'BAR',
          });
        },
      });
      mw = createLogicMiddleware([logicA]);
      mw.monitor$.subscribe((x) => monArr.push(x));
      const storeFn = mw({ dispatch })(next);
      storeFn(actionIgnore);
      storeFn(actionA);
      mw.whenComplete(bDone);
    });

    it('both messages hit next, one bypassed validation/transform', () => {
      expect(next.calls.length).toBe(2);
      expect(next.calls[0].arguments[0]).toEqual(actionIgnore);
      expect(next.calls[1].arguments[0]).toEqual(actionAResult);
    });

    it('only matching is processed and dispatched', () => {
      expect(dispatch.calls.length).toBe(1);
      expect(dispatch.calls[0].arguments[0]).toEqual(actionADispatch);
    });

    it('mw.monitor$ should track flow', () => {
      expect(monArr).toEqual([
        { action: { type: 'CAT' }, op: 'top' },
        { nextAction: { type: 'CAT' }, op: 'bottom' },
        { action: { type: 'FOO' }, op: 'top' },
        { action: { type: 'FOO' }, name: 'L(FOO)-0', op: 'begin' },
        {
          action: { type: 'FOO' },
          nextAction: { type: 'FOO', allowed: ['a'] },
          name: 'L(FOO)-0',
          shouldProcess: true,
          op: 'next',
        },
        { nextAction: { type: 'FOO', allowed: ['a'] }, op: 'bottom' },
        { action: { type: 'FOO' }, dispAction: { type: 'BAR', allowed: ['a'] }, op: 'dispatch' },
        { action: { type: 'FOO' }, name: 'L(FOO)-0', op: 'end' },
      ]);
    });

    it('mw.whenComplete(fn) should be called when complete', (bDone) => {
      mw.whenComplete(bDone);
    });

    it('mw.whenComplete(fn) should resolve to promise', (bDone) => {
      function fn() {}
      mw.whenComplete(fn).then(bDone);
    });

    it('mw.whenComplete() should resolve to promise', (bDone) => {
      mw.whenComplete().then(bDone);
    });

    it('delayed mw.whenComplete() should still resolve to promise', (bDone) => {
      setTimeout(() => {
        mw.whenComplete().then(bDone);
      }, 100);
    });
  });

  describe('[logicA] type is arr of strings, match any', () => {
    let mw;
    let logicA;
    let next;
    let dispatch;
    const actionA = { type: 'FOO' };
    const actionAResult = { type: 'FOO', allowed: ['a'] };
    const actionADispatch = { type: 'BAR', allowed: ['a'] };
    const actionIgnore = { type: 'CAT' };
    beforeEach((bDone) => {
      next = expect.createSpy();
      dispatch = expect.createSpy().andCall(() => bDone());
      logicA = createLogic({
        type: ['DOG', 'FOO'],
        validate({ action }, allow) {
          allow({
            ...action,
            allowed: ['a'],
          });
        },
        process({ action }, dispatch) {
          dispatch({
            ...action,
            type: 'BAR',
          });
        },
      });
      mw = createLogicMiddleware([logicA]);
      const storeFn = mw({ dispatch })(next);
      storeFn(actionIgnore);
      storeFn(actionA);
    });

    it('both messages hit next, one bypassed validation/transform', () => {
      expect(next.calls.length).toBe(2);
      expect(next.calls[0].arguments[0]).toEqual(actionIgnore);
      expect(next.calls[1].arguments[0]).toEqual(actionAResult);
    });

    it('only matching is processed and dispatched', () => {
      expect(dispatch.calls.length).toBe(1);
      expect(dispatch.calls[0].arguments[0]).toEqual(actionADispatch);
    });
  });

  describe('[logicA] type is symbol, match only', () => {
    let monArr = [];
    let mw;
    let logicA;
    let next;
    let dispatch;
    const FOO = Symbol('FOO');
    const BAR = Symbol('BAR');
    const CAT = Symbol('CAT');
    const actionA = { type: FOO };
    const actionAResult = { type: FOO, allowed: ['a'] };
    const actionADispatch = { type: BAR, allowed: ['a'] };
    const actionIgnore = { type: CAT };
    beforeEach((bDone) => {
      monArr = [];
      next = expect.createSpy();
      dispatch = expect.createSpy();
      logicA = createLogic({
        type: FOO,
        validate({ action }, allow) {
          allow({
            ...action,
            allowed: ['a'],
          });
        },
        process({ action }, dispatch) {
          dispatch({
            ...action,
            type: BAR,
          });
        },
      });
      mw = createLogicMiddleware([logicA]);
      mw.monitor$.subscribe((x) => monArr.push(x));
      const storeFn = mw({ dispatch })(next);
      storeFn(actionIgnore);
      storeFn(actionA);
      mw.whenComplete(bDone);
    });

    it('both messages hit next, one bypassed validation/transform', () => {
      expect(next.calls.length).toBe(2);
      expect(next.calls[0].arguments[0]).toEqual(actionIgnore);
      expect(next.calls[1].arguments[0]).toEqual(actionAResult);
    });

    it('only matching is processed and dispatched', () => {
      expect(dispatch.calls.length).toBe(1);
      expect(dispatch.calls[0].arguments[0]).toEqual(actionADispatch);
    });

    it('mw.monitor$ should track flow', () => {
      expect(monArr).toEqual([
        { action: { type: CAT }, op: 'top' },
        { nextAction: { type: CAT }, op: 'bottom' },
        { action: { type: FOO }, op: 'top' },
        { action: { type: FOO }, name: 'L(Symbol(FOO))-0', op: 'begin' },
        {
          action: { type: FOO },
          nextAction: { type: FOO, allowed: ['a'] },
          name: 'L(Symbol(FOO))-0',
          shouldProcess: true,
          op: 'next',
        },
        { nextAction: { type: FOO, allowed: ['a'] }, op: 'bottom' },
        { action: { type: FOO }, dispAction: { type: BAR, allowed: ['a'] }, op: 'dispatch' },
        { action: { type: FOO }, name: 'L(Symbol(FOO))-0', op: 'end' },
      ]);
    });

    it('mw.whenComplete(fn) should be called when complete', (bDone) => {
      mw.whenComplete(bDone);
    });

    it('mw.whenComplete(fn) should resolve to promise', (bDone) => {
      function fn() {}
      mw.whenComplete(fn).then(bDone);
    });

    it('mw.whenComplete() should resolve to promise', (bDone) => {
      mw.whenComplete().then(bDone);
    });

    it('delayed mw.whenComplete() should still resolve to promise', (bDone) => {
      setTimeout(() => {
        mw.whenComplete().then(bDone);
      }, 100);
    });
  });

  describe('[logicA] type is arr of Symbols, match any', () => {
    let mw;
    let logicA;
    let next;
    let dispatch;
    const FOO = Symbol('FOO');
    const BAR = Symbol('BAR');
    const CAT = Symbol('CAT');
    const DOG = Symbol('DOG');
    const actionA = { type: FOO };
    const actionAResult = { type: FOO, allowed: ['a'] };
    const actionADispatch = { type: BAR, allowed: ['a'] };
    const actionIgnore = { type: CAT };
    beforeEach((bDone) => {
      next = expect.createSpy();
      dispatch = expect.createSpy().andCall(() => bDone());
      logicA = createLogic({
        type: [DOG, FOO],
        validate({ action }, allow) {
          allow({
            ...action,
            allowed: ['a'],
          });
        },
        process({ action }, dispatch) {
          dispatch({
            ...action,
            type: BAR,
          });
        },
      });
      mw = createLogicMiddleware([logicA]);
      const storeFn = mw({ dispatch })(next);
      storeFn(actionIgnore);
      storeFn(actionA);
    });

    it('both messages hit next, one bypassed validation/transform', () => {
      expect(next.calls.length).toBe(2);
      expect(next.calls[0].arguments[0]).toEqual(actionIgnore);
      expect(next.calls[1].arguments[0]).toEqual(actionAResult);
    });

    it('only matching is processed and dispatched', () => {
      expect(dispatch.calls.length).toBe(1);
      expect(dispatch.calls[0].arguments[0]).toEqual(actionADispatch);
    });
  });

  describe('[logicA] type is regex, match', () => {
    let mw;
    let logicA;
    let next;
    let dispatch;
    const actionA = { type: 'FOO' };
    const actionAResult = { type: 'FOO', allowed: ['a'] };
    const actionADispatch = { type: 'BAR', allowed: ['a'] };
    const actionIgnore = { type: 'CAT' };
    beforeEach((bDone) => {
      next = expect.createSpy();
      dispatch = expect.createSpy().andCall(() => bDone());
      logicA = createLogic({
        type: /^F.*$/,
        validate({ action }, allow) {
          allow({
            ...action,
            allowed: ['a'],
          });
        },
        process({ action }, dispatch) {
          dispatch({
            ...action,
            type: 'BAR',
          });
        },
      });
      mw = createLogicMiddleware([logicA]);
      const storeFn = mw({ dispatch })(next);
      storeFn(actionIgnore);
      storeFn(actionA);
    });

    it('both messages hit next, one bypassed validation/transform', () => {
      expect(next.calls.length).toBe(2);
      expect(next.calls[0].arguments[0]).toEqual(actionIgnore);
      expect(next.calls[1].arguments[0]).toEqual(actionAResult);
    });

    it('only matching is processed and dispatched', () => {
      expect(dispatch.calls.length).toBe(1);
      expect(dispatch.calls[0].arguments[0]).toEqual(actionADispatch);
    });
  });

  // arr of regex
  describe('[logicA] type is arr of regex, match', () => {
    let mw;
    let logicA;
    let next;
    let dispatch;
    const actionA = { type: 'FOO' };
    const actionAResult = { type: 'FOO', allowed: ['a'] };
    const actionADispatch = { type: 'BAR', allowed: ['a'] };
    const actionIgnore = { type: 'CAT' };
    beforeEach((bDone) => {
      next = expect.createSpy();
      dispatch = expect.createSpy().andCall(() => bDone());
      logicA = createLogic({
        type: [/^D.*$/, /^F.*$/],
        validate({ action }, allow) {
          allow({
            ...action,
            allowed: ['a'],
          });
        },
        process({ action }, dispatch) {
          dispatch({
            ...action,
            type: 'BAR',
          });
        },
      });
      mw = createLogicMiddleware([logicA]);
      const storeFn = mw({ dispatch })(next);
      storeFn(actionIgnore);
      storeFn(actionA);
    });

    it('both messages hit next, one bypassed validation/transform', () => {
      expect(next.calls.length).toBe(2);
      expect(next.calls[0].arguments[0]).toEqual(actionIgnore);
      expect(next.calls[1].arguments[0]).toEqual(actionAResult);
    });

    it('only matching is processed and dispatched', () => {
      expect(dispatch.calls.length).toBe(1);
      expect(dispatch.calls[0].arguments[0]).toEqual(actionADispatch);
    });
  });

  describe('[logicA] type is arr of string and regex, match', () => {
    let mw;
    let logicA;
    let next;
    let dispatch;
    const actionA = { type: 'FOO' };
    const actionAResult = { type: 'FOO', allowed: ['a'] };
    const actionADispatch = { type: 'BAR', allowed: ['a'] };
    const actionIgnore = { type: 'CAT' };
    beforeEach((bDone) => {
      next = expect.createSpy();
      dispatch = expect.createSpy().andCall(() => bDone());
      logicA = createLogic({
        type: ['EGG', 'FROG', 'FOO', /^D.*$/],
        validate({ action }, allow) {
          allow({
            ...action,
            allowed: ['a'],
          });
        },
        process({ action }, dispatch) {
          dispatch({
            ...action,
            type: 'BAR',
          });
        },
      });
      mw = createLogicMiddleware([logicA]);
      const storeFn = mw({ dispatch })(next);
      storeFn(actionIgnore);
      storeFn(actionA);
    });

    it('both messages hit next, one bypassed validation/transform', () => {
      expect(next.calls.length).toBe(2);
      expect(next.calls[0].arguments[0]).toEqual(actionIgnore);
      expect(next.calls[1].arguments[0]).toEqual(actionAResult);
    });

    it('only matching is processed and dispatched', () => {
      expect(dispatch.calls.length).toBe(1);
      expect(dispatch.calls[0].arguments[0]).toEqual(actionADispatch);
    });
  });

  describe('[logicA] type is *, matches all', () => {
    let mw;
    let logicA;
    let next;
    let dispatch;
    const actionA = { type: 'FOO' };
    const actionAResult = { type: 'FOO', allowed: ['a'] };
    const actionADispatch = { type: 'BAR', allowed: ['a'] };
    const actionCat = { type: 'CAT', id: 2 };
    const actionCatResult = { type: 'CAT', id: 2, allowed: ['a'] };
    const actionCatDispatch = { type: 'BAR', id: 2, allowed: ['a'] };
    beforeEach((bDone) => {
      next = expect.createSpy();
      dispatch = expect.createSpy().andCall(cb);
      let dispatchCount = 0;
      function cb() {
        if (++dispatchCount >= 2) {
          bDone();
        }
      }
      logicA = createLogic({
        type: '*',
        validate({ action }, allow) {
          allow({
            ...action,
            allowed: ['a'],
          });
        },
        process({ action }, dispatch) {
          dispatch({
            ...action,
            type: 'BAR',
          });
        },
      });
      mw = createLogicMiddleware([logicA]);
      const storeFn = mw({ dispatch })(next);
      storeFn(actionA);
      storeFn(actionCat);
    });

    it('both messages match', () => {
      expect(next.calls.length).toBe(2);
      expect(next.calls[0].arguments[0]).toEqual(actionAResult);
      expect(next.calls[1].arguments[0]).toEqual(actionCatResult);
    });

    it('both dispatched', () => {
      expect(dispatch.calls.length).toBe(2);
      expect(dispatch.calls[0].arguments[0]).toEqual(actionADispatch);
      expect(dispatch.calls[1].arguments[0]).toEqual(actionCatDispatch);
    });
  });

  describe('[logicA] validate allow', () => {
    let monArr = [];
    let mw;
    let logicA;
    let next;
    const actionAllow = { type: 'FOO', allowMe: true };
    const actionA = { type: 'FOO', allowMe: true, allowed: ['a'] };
    beforeEach((bDone) => {
      monArr = [];
      next = expect.createSpy();
      logicA = createLogic({
        type: 'FOO',
        validate({ action }, allow, reject) {
          if (action.allowMe) {
            allow({
              ...action,
              allowed: ['a'],
            });
          } else {
            reject(action);
          }
        },
      });
      mw = createLogicMiddleware([logicA]);
      mw.monitor$.subscribe((x) => monArr.push(x));
      mw({})(next)(actionAllow);
      mw.whenComplete(bDone);
    });

    it('allow and augment action', () => {
      expect(next.calls.length).toBe(1);
      expect(next.calls[0].arguments[0]).toEqual(actionA);
    });

    it('mw.monitor$ should track flow', () => {
      expect(monArr).toEqual([
        { action: { type: 'FOO', allowMe: true }, op: 'top' },
        { action: { type: 'FOO', allowMe: true }, name: 'L(FOO)-0', op: 'begin' },
        {
          action: { type: 'FOO', allowMe: true },
          nextAction: { type: 'FOO', allowMe: true, allowed: ['a'] },
          name: 'L(FOO)-0',
          shouldProcess: true,
          op: 'next',
        },
        { nextAction: { type: 'FOO', allowMe: true, allowed: ['a'] }, op: 'bottom' },
        { action: { type: 'FOO', allowMe: true }, name: 'L(FOO)-0', op: 'end' },
      ]);
    });
  });

  describe('[logicA] validate reject', () => {
    let monArr = [];
    let mw;
    let logicA;
    let next;
    const actionReject = { type: 'FOO', allowMe: false };
    const actionA = { type: 'FOO', allowMe: false, rejected: ['a'] };
    beforeEach((bDone) => {
      monArr = [];
      next = expect.createSpy();
      logicA = createLogic({
        name: 'logicA',
        type: 'FOO',
        validate({ action }, allow, reject) {
          if (action.allowMe) {
            allow({
              ...action,
              allowed: ['a'],
            });
          } else {
            reject({
              ...action,
              rejected: ['a'],
            });
          }
        },
      });
      mw = createLogicMiddleware([logicA]);
      mw.monitor$.subscribe((x) => monArr.push(x));
      mw({})(next)(actionReject);
      mw.whenComplete(bDone);
    });

    it('reject and augment action', () => {
      expect(next.calls.length).toBe(1);
      expect(next.calls[0].arguments[0]).toEqual(actionA);
    });

    it('mw.monitor$ should track flow', () => {
      expect(monArr).toEqual([
        { action: { type: 'FOO', allowMe: false }, op: 'top' },
        { action: { type: 'FOO', allowMe: false }, name: 'logicA', op: 'begin' },
        {
          action: { type: 'FOO', allowMe: false },
          nextAction: { type: 'FOO', allowMe: false, rejected: ['a'] },
          name: 'logicA',
          shouldProcess: false,
          op: 'next',
        },
        { nextAction: { type: 'FOO', allowMe: false, rejected: ['a'] }, op: 'bottom' },
        { action: { type: 'FOO', allowMe: false }, name: 'logicA', op: 'end' },
      ]);
    });
  });

  describe('[logicA] validate allow undefined', () => {
    let monArr = [];
    let mw;
    let logicA;
    let next;
    const actionAllow = { type: 'FOO', allowMe: true };
    beforeEach((bDone) => {
      monArr = [];
      next = expect.createSpy();
      logicA = createLogic({
        type: 'FOO',
        validate({ action }, allow, reject) {
          if (action.allowMe) {
            allow();
          } else {
            reject(action);
          }
        },
      });
      mw = createLogicMiddleware([logicA]);
      mw.monitor$.subscribe((x) => monArr.push(x));
      mw({})(next)(actionAllow);
      mw.whenComplete(bDone);
    });

    it('allow and augment action', () => {
      expect(next.calls.length).toBe(0);
    });

    it('mw.monitor$ should track flow', () => {
      expect(monArr).toEqual([
        { action: { type: 'FOO', allowMe: true }, op: 'top' },
        { action: { type: 'FOO', allowMe: true }, name: 'L(FOO)-0', op: 'begin' },
        { action: { type: 'FOO', allowMe: true }, name: 'L(FOO)-0', shouldProcess: true, op: 'filtered' },
        {
          action: {
            allowMe: true,
            type: 'FOO',
          },
          name: 'L(FOO)-0',
          op: 'end',
        },
      ]);
    });
  });

  describe('[logicA] validate reject undefined', () => {
    let monArr = [];
    let mw;
    let logicA;
    let next;
    const actionReject = { type: 'FOO', allowMe: false };
    beforeEach((bDone) => {
      monArr = [];
      next = expect.createSpy();
      logicA = createLogic({
        name: 'logicA',
        type: 'FOO',
        validate({ action }, allow, reject) {
          if (action.allowMe) {
            allow({
              ...action,
              allowed: ['a'],
            });
          } else {
            reject();
          }
        },
      });
      mw = createLogicMiddleware([logicA]);
      mw.monitor$.subscribe((x) => monArr.push(x));
      mw({})(next)(actionReject);
      mw.whenComplete(bDone);
    });

    it('reject and augment action', () => {
      expect(next.calls.length).toBe(0);
    });

    it('mw.monitor$ should track flow', () => {
      expect(monArr).toEqual([
        { action: { type: 'FOO', allowMe: false }, op: 'top' },
        { action: { type: 'FOO', allowMe: false }, name: 'logicA', op: 'begin' },
        { action: { type: 'FOO', allowMe: false }, name: 'logicA', shouldProcess: false, op: 'filtered' },
        { action: { type: 'FOO', allowMe: false }, name: 'logicA', op: 'end' },
      ]);
    });

    it('mw.whenComplete(fn) should be called when complete', (bDone) => {
      mw.whenComplete(bDone);
    });

    it('mw.whenComplete(fn) should resolve to promise', (bDone) => {
      function fn() {}
      mw.whenComplete(fn).then(bDone);
    });

    it('mw.whenComplete() should resolve to promise', (bDone) => {
      mw.whenComplete().then(bDone);
    });
  });

  describe('[logicA] validate+process allow same', () => {
    let monArr = [];
    let mw;
    let logicA;
    let next;
    let dispatch;
    const actionAllow = { type: 'FOO', allowMe: true };
    const actionA = { type: 'FOO', allowMe: true, allowed: ['a'] };
    const actionBar = { type: 'BAR', allowMe: true, allowed: ['a'], processed: ['a'] };
    beforeEach((bDone) => {
      monArr = [];
      next = expect.createSpy();
      dispatch = expect.createSpy();
      logicA = createLogic({
        type: 'FOO',
        validate({ action }, allow, reject) {
          if (action.allowMe) {
            allow({
              ...action,
              allowed: ['a'],
            });
          } else {
            reject(action);
          }
        },
        process({ action, cancelled$ }, dispatch) {
          cancelled$.subscribe({ complete: () => bDone() });
          dispatch({
            ...action,
            type: 'BAR',
            processed: ['a'],
          });
        },
      });
      mw = createLogicMiddleware([logicA]);
      mw.monitor$.subscribe((x) => monArr.push(x));
      mw({ dispatch })(next)(actionAllow);
    });

    it('allow, augment, and transform action', () => {
      expect(next.calls.length).toBe(1);
      expect(next.calls[0].arguments[0]).toEqual(actionA);
    });

    it('dispatch actionBar', () => {
      expect(dispatch.calls.length).toBe(1);
      expect(dispatch.calls[0].arguments[0]).toEqual(actionBar);
    });

    it('mw.monitor$ should track flow', () => {
      expect(monArr).toEqual([
        { action: { type: 'FOO', allowMe: true }, op: 'top' },
        { action: { type: 'FOO', allowMe: true }, name: 'L(FOO)-0', op: 'begin' },
        {
          action: { type: 'FOO', allowMe: true },
          nextAction: { type: 'FOO', allowMe: true, allowed: ['a'] },
          name: 'L(FOO)-0',
          shouldProcess: true,
          op: 'next',
        },
        { nextAction: { type: 'FOO', allowMe: true, allowed: ['a'] }, op: 'bottom' },
        {
          action: { type: 'FOO', allowMe: true },
          dispAction: { type: 'BAR', allowMe: true, allowed: ['a'], processed: ['a'] },
          op: 'dispatch',
        },
        { action: { type: 'FOO', allowMe: true }, name: 'L(FOO)-0', op: 'end' },
      ]);
    });

    it('mw.whenComplete(fn) should be called when complete', (bDone) => {
      mw.whenComplete(bDone);
    });

    it('mw.whenComplete(fn) should resolve to promise', (bDone) => {
      function fn() {}
      mw.whenComplete(fn).then(bDone);
    });

    it('mw.whenComplete() should resolve to promise', (bDone) => {
      mw.whenComplete().then(bDone);
    });
  });

  describe('[logicA] validate+process allow same useDispatch=true', () => {
    let monArr = [];
    let mw;
    let logicA;
    let next;
    let dispatch;
    const actionAllow = { type: 'FOO', allowMe: true };
    const actionA = { type: 'FOO', allowMe: true, allowed: ['a'] };
    const actionProcess = { type: 'DOG' };
    beforeEach((bDone) => {
      monArr = [];
      next = expect.createSpy();
      dispatch = expect.createSpy().andCall(cb);
      let dispatchCalled = 0;
      function cb() {
        if (++dispatchCalled >= 2) {
          // calling bDone with mw.whenComplete below
        }
      }
      logicA = createLogic({
        type: 'FOO',
        validate({ action }, allow, reject) {
          if (action.allowMe) {
            allow(
              {
                ...action,
                allowed: ['a'],
              },
              { useDispatch: true }
            );
          } else {
            reject(action);
          }
        },
        process({ action }, dispatch) {
          dispatch(actionProcess);
        },
      });
      mw = createLogicMiddleware([logicA]);
      mw.monitor$.subscribe((x) => monArr.push(x));
      mw({ dispatch })(next)(actionAllow);
      mw.whenComplete(bDone);
    });

    it('allow will not call next since useDispatch=true', () => {
      expect(next.calls.length).toBe(0);
    });

    it('dispatch should be called twice for allow and process', () => {
      expect(dispatch.calls.length).toBe(2);
      expect(dispatch.calls[0].arguments[0]).toEqual(actionA);
      expect(dispatch.calls[1].arguments[0]).toEqual(actionProcess);
    });

    it('mw.monitor$ should track flow', () => {
      expect(monArr).toEqual([
        { action: { type: 'FOO', allowMe: true }, op: 'top' },
        { action: { type: 'FOO', allowMe: true }, name: 'L(FOO)-0', op: 'begin' },
        {
          action: { type: 'FOO', allowMe: true },
          dispAction: { type: 'FOO', allowMe: true, allowed: ['a'] },
          name: 'L(FOO)-0',
          shouldProcess: true,
          op: 'nextDisp',
        },
        {
          action: { type: 'FOO', allowMe: true },
          dispAction: { type: 'FOO', allowMe: true, allowed: ['a'] },
          op: 'dispatch',
        },
        { action: { type: 'FOO', allowMe: true }, dispAction: { type: 'DOG' }, op: 'dispatch' },
        { action: { type: 'FOO', allowMe: true }, name: 'L(FOO)-0', op: 'end' },
      ]);
    });
  });

  describe('[logicA] validate+process allow same useDispatch=false', () => {
    let mw;
    let logicA;
    let next;
    let dispatch;
    const actionAllow = { type: 'FOO', allowMe: true };
    const actionA = { type: 'FOO', allowMe: true, allowed: ['a'] };
    const actionBar = { type: 'BAR', allowMe: true, allowed: ['a'], processed: ['a'] };
    beforeEach((bDone) => {
      next = expect.createSpy();
      dispatch = expect.createSpy().andCall(() => bDone());
      logicA = createLogic({
        type: 'FOO',
        validate({ action }, allow, reject) {
          if (action.allowMe) {
            allow(
              {
                ...action,
                allowed: ['a'],
              },
              { useDispatch: false }
            );
          } else {
            reject(action);
          }
        },
        process({ action }, dispatch) {
          dispatch({
            ...action,
            type: 'BAR',
            processed: ['a'],
          });
        },
      });
      mw = createLogicMiddleware([logicA]);
      mw({ dispatch })(next)(actionAllow);
    });

    it('allow, augment, and transform action', () => {
      expect(next.calls.length).toBe(1);
      expect(next.calls[0].arguments[0]).toEqual(actionA);
    });

    it('dispatch actionBar', () => {
      expect(dispatch.calls.length).toBe(1);
      expect(dispatch.calls[0].arguments[0]).toEqual(actionBar);
    });

    it('mw.whenComplete(fn) should be called when complete', (bDone) => {
      mw.whenComplete(bDone);
    });

    it('mw.whenComplete(fn) should resolve to promise', (bDone) => {
      function fn() {}
      mw.whenComplete(fn).then(bDone);
    });

    it('mw.whenComplete() should resolve to promise', (bDone) => {
      mw.whenComplete().then(bDone);
    });
  });

  describe('[logicA] validate+process allow diff', () => {
    let monArr = [];
    let mw;
    let logicA;
    let next;
    let dispatch;
    const actionAllow = { type: 'FOO', allowMe: true };
    const actionA = { type: 'CAT', allowMe: true, allowed: ['a'] };
    const actionProcess = { type: 'DOG' };
    beforeEach((bDone) => {
      monArr = [];
      next = expect.createSpy();
      dispatch = expect.createSpy().andCall(cb);
      let dispatchCalled = 0;
      function cb() {
        if (++dispatchCalled >= 2) {
          /* bDone called by whenComplete */
        }
      }
      logicA = createLogic({
        type: 'FOO',
        validate({ action }, allow, reject) {
          if (action.allowMe) {
            allow({
              ...action,
              type: 'CAT', // changing type
              allowed: ['a'],
            });
          } else {
            reject(action);
          }
        },
        process({ action }, dispatch) {
          dispatch(actionProcess);
        },
      });
      mw = createLogicMiddleware([logicA]);
      mw.monitor$.subscribe((x) => monArr.push(x));
      mw({ dispatch })(next)(actionAllow);
      mw.whenComplete(bDone);
    });

    it('allow will not call next since type was diff', () => {
      expect(next.calls.length).toBe(0);
    });

    it('dispatch should be called twice for allow and process', () => {
      expect(dispatch.calls.length).toBe(2);
      expect(dispatch.calls[0].arguments[0]).toEqual(actionA);
      expect(dispatch.calls[1].arguments[0]).toEqual(actionProcess);
    });

    it('mw.monitor$ should track flow', () => {
      expect(monArr).toEqual([
        { action: { type: 'FOO', allowMe: true }, op: 'top' },
        { action: { type: 'FOO', allowMe: true }, name: 'L(FOO)-0', op: 'begin' },
        {
          action: { type: 'FOO', allowMe: true },
          dispAction: { type: 'CAT', allowMe: true, allowed: ['a'] },
          name: 'L(FOO)-0',
          shouldProcess: true,
          op: 'nextDisp',
        },
        {
          action: { type: 'FOO', allowMe: true },
          dispAction: { type: 'CAT', allowMe: true, allowed: ['a'] },
          op: 'dispatch',
        },
        { action: { type: 'FOO', allowMe: true }, dispAction: { type: 'DOG' }, op: 'dispatch' },
        { action: { type: 'FOO', allowMe: true }, name: 'L(FOO)-0', op: 'end' },
      ]);
    });

    it('mw.whenComplete(fn) should be called when complete', (bDone) => {
      mw.whenComplete(bDone);
    });

    it('mw.whenComplete(fn) should resolve to promise', (bDone) => {
      function fn() {}
      mw.whenComplete(fn).then(bDone);
    });

    it('mw.whenComplete() should resolve to promise', (bDone) => {
      mw.whenComplete().then(bDone);
    });
  });

  describe('[logicA] validate+process allow diff useDispatch=true', () => {
    let mw;
    let logicA;
    let next;
    let dispatch;
    const actionAllow = { type: 'FOO', allowMe: true };
    const actionA = { type: 'CAT', allowMe: true, allowed: ['a'] };
    const actionProcess = { type: 'DOG' };
    beforeEach((bDone) => {
      next = expect.createSpy();
      dispatch = expect.createSpy().andCall(cb);
      let dispatchCalled = 0;
      function cb() {
        if (++dispatchCalled >= 2) {
          bDone();
        }
      }
      logicA = createLogic({
        type: 'FOO',
        validate({ action }, allow, reject) {
          if (action.allowMe) {
            allow(
              {
                ...action,
                type: 'CAT', // changing type
                allowed: ['a'],
              },
              { useDispatch: true }
            );
          } else {
            reject(action);
          }
        },
        process({ action }, dispatch) {
          dispatch(actionProcess);
        },
      });
      mw = createLogicMiddleware([logicA]);
      mw({ dispatch })(next)(actionAllow);
    });

    it('allow will not call next since useDispatch = true', () => {
      expect(next.calls.length).toBe(0);
    });

    it('dispatch should be called twice for allow and process', () => {
      expect(dispatch.calls.length).toBe(2);
      expect(dispatch.calls[0].arguments[0]).toEqual(actionA);
      expect(dispatch.calls[1].arguments[0]).toEqual(actionProcess);
    });

    it('mw.whenComplete(fn) should be called when complete', (bDone) => {
      mw.whenComplete(bDone);
    });

    it('mw.whenComplete(fn) should resolve to promise', (bDone) => {
      function fn() {}
      mw.whenComplete(fn).then(bDone);
    });

    it('mw.whenComplete() should resolve to promise', (bDone) => {
      mw.whenComplete().then(bDone);
    });
  });

  describe('[logicA] validate+process allow diff useDispatch=false', () => {
    let mw;
    let logicA;
    let next;
    let dispatch;
    const actionAllow = { type: 'FOO', allowMe: true };
    const actionA = { type: 'CAT', allowMe: true, allowed: ['a'] };
    const actionProcess = { type: 'DOG' };
    beforeEach((bDone) => {
      next = expect.createSpy();
      dispatch = expect.createSpy().andCall(cb);
      let dispatchCalled = 0;
      function cb() {
        if (++dispatchCalled >= 1) {
          bDone();
        }
      }
      logicA = createLogic({
        type: 'FOO',
        validate({ action }, allow, reject) {
          if (action.allowMe) {
            allow(
              {
                ...action,
                type: 'CAT', // changing type
                allowed: ['a'],
              },
              { useDispatch: false }
            );
          } else {
            reject(action);
          }
        },
        process({ action }, dispatch) {
          dispatch(actionProcess);
        },
      });
      mw = createLogicMiddleware([logicA]);
      mw({ dispatch })(next)(actionAllow);
    });

    it('allow will call next since useDispatch = false', () => {
      expect(next.calls.length).toBe(1);
      expect(next.calls[0].arguments[0]).toEqual(actionA);
    });

    it('dispatch should be called for process', () => {
      expect(dispatch.calls.length).toBe(1);
      expect(dispatch.calls[0].arguments[0]).toEqual(actionProcess);
    });

    it('mw.whenComplete(fn) should be called when complete', (bDone) => {
      mw.whenComplete(bDone);
    });

    it('mw.whenComplete(fn) should resolve to promise', (bDone) => {
      function fn() {}
      mw.whenComplete(fn).then(bDone);
    });

    it('mw.whenComplete() should resolve to promise', (bDone) => {
      mw.whenComplete().then(bDone);
    });
  });

  describe('[logicA] validate+process reject same', () => {
    let monArr = [];
    let mw;
    let logicA;
    let next;
    let dispatch;
    const actionReject = { type: 'FOO', allowMe: false };
    const actionA = { type: 'FOO', allowMe: false, rejected: ['a'] };
    beforeEach((bDone) => {
      monArr = [];
      next = expect.createSpy();
      dispatch = expect.createSpy().andCall(() => bDone(new Error('should not call')));
      logicA = createLogic({
        type: 'FOO',
        validate({ action }, allow, reject) {
          reject({
            ...action,
            rejected: ['a'],
          });
        },
        process({ action }, dispatch) {
          dispatch({
            ...action,
            type: 'BAR',
            processed: ['a'],
          });
        },
      });
      mw = createLogicMiddleware([logicA]);
      mw.monitor$.subscribe((x) => monArr.push(x));
      mw({ dispatch })(next)(actionReject);
      mw.whenComplete(bDone);
    });

    it('reject, augment, and transform action', () => {
      expect(next.calls.length).toBe(1);
      expect(next.calls[0].arguments[0]).toEqual(actionA);
    });

    it('not dispatch anything', () => {
      expect(dispatch.calls.length).toBe(0);
    });

    it('mw.monitor$ should track flow', () => {
      expect(monArr).toEqual([
        { action: { type: 'FOO', allowMe: false }, op: 'top' },
        { action: { type: 'FOO', allowMe: false }, name: 'L(FOO)-0', op: 'begin' },
        {
          action: { type: 'FOO', allowMe: false },
          nextAction: { type: 'FOO', allowMe: false, rejected: ['a'] },
          name: 'L(FOO)-0',
          shouldProcess: false,
          op: 'next',
        },
        { nextAction: { type: 'FOO', allowMe: false, rejected: ['a'] }, op: 'bottom' },
        { action: { type: 'FOO', allowMe: false }, name: 'L(FOO)-0', op: 'end' },
      ]);
    });

    it('mw.whenComplete(fn) should be called when complete', (bDone) => {
      mw.whenComplete(bDone);
    });

    it('mw.whenComplete(fn) should resolve to promise', (bDone) => {
      function fn() {}
      mw.whenComplete(fn).then(bDone);
    });

    it('mw.whenComplete() should resolve to promise', (bDone) => {
      mw.whenComplete().then(bDone);
    });
  });

  describe('[logicA] validate+process reject diff', () => {
    let mw;
    let logicA;
    let next;
    let dispatch;
    const actionReject = { type: 'FOO', allowMe: false };
    const actionA = { type: 'BAR', allowMe: false, rejected: ['a'] };
    beforeEach((bDone) => {
      next = expect.createSpy().andCall(() => bDone(new Error('should not call next')));
      dispatch = expect.createSpy().andCall(() => bDone());
      logicA = createLogic({
        type: 'FOO',
        validate({ action }, allow, reject) {
          if (action.allowMe) {
            allow({
              ...action,
              allowed: ['a'],
            });
          } else {
            reject({
              ...action,
              type: 'BAR',
              rejected: ['a'],
            });
          }
        },
        process({ action }, dispatch) {
          dispatch({
            ...action,
            type: 'BAR',
            processed: ['a'],
          });
        },
      });
      mw = createLogicMiddleware([logicA]);
      mw({ dispatch })(next)(actionReject);
    });

    it('reject with diff type should not call next', () => {
      expect(next.calls.length).toBe(0);
    });

    it('reject diff type should dispatch, no process dispatch', () => {
      expect(dispatch.calls.length).toBe(1);
      expect(dispatch.calls[0].arguments[0]).toEqual(actionA);
    });

    it('mw.whenComplete(fn) should be called when complete', (bDone) => {
      mw.whenComplete(bDone);
    });

    it('mw.whenComplete(fn) should resolve to promise', (bDone) => {
      function fn() {}
      mw.whenComplete(fn).then(bDone);
    });

    it('mw.whenComplete() should resolve to promise', (bDone) => {
      mw.whenComplete().then(bDone);
    });
  });

  describe('[logicA] transform', () => {
    let mw;
    let logicA;
    let next;
    const action = { type: 'FOO' };
    const actionA = { type: 'FOO', a: 1 };
    beforeEach((bDone) => {
      next = expect.createSpy().andCall(() => bDone());
      logicA = createLogic({
        type: 'FOO',
        transform({ action }, next) {
          next({
            ...action,
            a: 1,
          });
        },
      });
      mw = createLogicMiddleware([logicA]);
      mw({})(next)(action);
    });

    it('transforms the action', () => {
      expect(next.calls.length).toBe(1);
      expect(next.calls[0].arguments[0]).toEqual(actionA);
    });
  });

  describe('createLogicMiddleware([logicA, logicB]) transforms', () => {
    let mw;
    let next;
    const action = { type: 'FOO', trans: [] };
    const actionAB = { type: 'FOO', trans: ['a', 'b'] };
    beforeEach((bDone) => {
      next = expect.createSpy().andCall(() => bDone());
      const logicA = createLogic({
        type: 'FOO',
        transform({ action }, next) {
          const trans = action.trans;
          next({
            ...action,
            trans: [...trans, 'a'],
          });
        },
      });
      const logicB = createLogic({
        type: 'FOO',
        transform({ action }, next) {
          const trans = action.trans;
          next({
            ...action,
            trans: [...trans, 'b'],
          });
        },
      });
      mw = createLogicMiddleware([logicA, logicB]);
      mw({})(next)(action);
    });

    it('is transformed through both logicA and logicB', () => {
      expect(next.calls.length).toBe(1);
      expect(next.calls[0].arguments[0]).toEqual(actionAB);
    });
  });

  describe('createLogicMiddleware([logicA(FOO), logicB(BAR|FOO]) foo transforms', () => {
    let monArr = [];
    let mw;
    let next;
    const action = { type: 'FOO', trans: [] };
    const actionAB = { type: 'FOO', trans: ['a', 'b'] };
    beforeEach((bDone) => {
      monArr = [];
      next = expect.createSpy();
      const logicA = createLogic({
        type: 'FOO',
        transform({ action }, next) {
          const trans = action.trans;
          next({
            ...action,
            trans: [...trans, 'a'],
          });
        },
        process({ action, cancelled$ }, dispatch) {
          // logicA is the last to exit since it is first
          cancelled$.subscribe({ complete: () => bDone() });
          dispatch();
        },
      });
      const logicB = createLogic({
        type: ['BAR', 'FOO'],
        transform({ action }, next) {
          const trans = action.trans;
          next({
            ...action,
            trans: [...trans, 'b'],
          });
        },
      });
      mw = createLogicMiddleware([logicA, logicB]);
      mw.monitor$.subscribe((x) => monArr.push(x));
      mw({})(next)(action);
    });

    it('is transformed through both logicA and logicB', () => {
      expect(next.calls.length).toBe(1);
      expect(next.calls[0].arguments[0]).toEqual(actionAB);
    });

    it('mw.monitor$ should track flow', () => {
      expect(monArr).toEqual([
        { action: { type: 'FOO', trans: [] }, op: 'top' },
        { action: { type: 'FOO', trans: [] }, name: 'L(FOO)-0', op: 'begin' },
        {
          action: { type: 'FOO', trans: [] },
          nextAction: { type: 'FOO', trans: ['a'] },
          name: 'L(FOO)-0',
          shouldProcess: true,
          op: 'next',
        },
        { action: { type: 'FOO', trans: ['a'] }, name: 'L(BAR,FOO)-1', op: 'begin' },
        {
          action: { type: 'FOO', trans: ['a'] },
          nextAction: { type: 'FOO', trans: ['a', 'b'] },
          name: 'L(BAR,FOO)-1',
          shouldProcess: true,
          op: 'next',
        },
        { nextAction: { type: 'FOO', trans: ['a', 'b'] }, op: 'bottom' },
        { action: { type: 'FOO', trans: ['a'] }, name: 'L(BAR,FOO)-1', op: 'end' },
        { action: { type: 'FOO', trans: [] }, name: 'L(FOO)-0', op: 'end' },
      ]);
    });

    it('mw.whenComplete(fn) should be called when complete', (bDone) => {
      mw.whenComplete(bDone);
    });

    it('mw.whenComplete(fn) should resolve to promise', (bDone) => {
      function fn() {}
      mw.whenComplete(fn).then(bDone);
    });

    it('mw.whenComplete() should resolve to promise', (bDone) => {
      mw.whenComplete().then(bDone);
    });

    it('delayed mw.whenComplete() should still resolve to promise', (bDone) => {
      setTimeout(() => {
        mw.whenComplete().then(bDone);
      }, 100);
    });
  });
});


text:
namespace extensions {

GaiaWebAuthFlow::GaiaWebAuthFlow(Delegate* delegate,
                                 Profile* profile,
                                 const ExtensionTokenKey* token_key,
                                 const std::string& oauth2_client_id,
                                 const std::string& locale)
    : delegate_(delegate),
      profile_(profile),
      account_id_(token_key->account_id) {
  TRACE_EVENT_NESTABLE_ASYNC_BEGIN2(
      "identity", "GaiaWebAuthFlow", this, "extension_id",
      token_key->extension_id, "account_id", token_key->account_id.ToString());

  const char kOAuth2RedirectPathFormat[] = "/%s";
  const char kOAuth2AuthorizeFormat[] =
      "?response_type=token&approval_prompt=force&authuser=0&"
      "client_id=%s&"
      "scope=%s&"
      "origin=chrome-extension://%s/&"
      "redirect_uri=%s:/%s&"
      "hl=%s";
  // Additional parameters to pass if device_id is enabled.
  const char kOAuth2AuthorizeFormatDeviceIdAddendum[] =
      "&device_id=%s&"
      "device_type=chrome";

  std::vector<base::StringPiece> scopes(token_key->scopes.begin(),
                                        token_key->scopes.end());
  std::vector<base::StringPiece> client_id_parts = base::SplitStringPiece(
      oauth2_client_id, ".", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
  std::reverse(client_id_parts.begin(), client_id_parts.end());
  redirect_scheme_ = base::JoinString(client_id_parts, ".");
  std::string signin_scoped_device_id;
  if (profile_)
    signin_scoped_device_id = GetSigninScopedDeviceIdForProfile(profile_);

  redirect_path_prefix_ = base::StringPrintf(kOAuth2RedirectPathFormat,
                                             token_key->extension_id.c_str());

  std::string oauth2_authorize_params = base::StringPrintf(
      kOAuth2AuthorizeFormat,
      oauth2_client_id.c_str(),
      net::EscapeUrlEncodedData(base::JoinString(scopes, " "), true).c_str(),
      token_key->extension_id.c_str(),
      redirect_scheme_.c_str(),
      token_key->extension_id.c_str(),
      locale.c_str());
  if (!signin_scoped_device_id.empty()) {
    oauth2_authorize_params += base::StringPrintf(
        kOAuth2AuthorizeFormatDeviceIdAddendum,
        net::EscapeUrlEncodedData(signin_scoped_device_id, true).c_str());
  }
  auth_url_ = GaiaUrls::GetInstance()->oauth2_auth_url().Resolve(
      oauth2_authorize_params);
}

GaiaWebAuthFlow::~GaiaWebAuthFlow() {
  TRACE_EVENT_NESTABLE_ASYNC_END0("identity", "GaiaWebAuthFlow", this);

  if (web_flow_)
    web_flow_.release()->DetachDelegateAndDelete();
}

void GaiaWebAuthFlow::Start() {
  TRACE_EVENT_NESTABLE_ASYNC_BEGIN0("identity", "UbertokenFetch", this);
  auto* identity_manager = IdentityManagerFactory::GetForProfile(profile_);
  ubertoken_fetcher_ = identity_manager->CreateUbertokenFetcherForAccount(
      account_id_,
      base::BindOnce(&GaiaWebAuthFlow::OnUbertokenFetchComplete,
                     base::Unretained(this)),
      gaia::GaiaSource::kChrome, profile_->GetURLLoaderFactory());
}

void GaiaWebAuthFlow::OnUbertokenFetchComplete(GoogleServiceAuthError error,
                                               const std::string& token) {
  if (error != GoogleServiceAuthError::AuthErrorNone()) {
    TRACE_EVENT_NESTABLE_ASYNC_END1("identity", "UbertokenFetch", this, "error",
                                    error.ToString());

    DVLOG(1) << "OnUbertokenFetchComplete failure: " << error.error_message();
    delegate_->OnGaiaFlowFailure(GaiaWebAuthFlow::SERVICE_AUTH_ERROR, error,
                                 std::string());
    return;
  }

  TRACE_EVENT_NESTABLE_ASYNC_END0("identity", "UbertokenFetch", this);

  const char kMergeSessionQueryFormat[] = "?uberauth=%s&"
                                          "continue=%s&"
                                          "source=appsv2";

  std::string merge_query = base::StringPrintf(
      kMergeSessionQueryFormat,
      net::EscapeUrlEncodedData(token, true).c_str(),
      net::EscapeUrlEncodedData(auth_url_.spec(), true).c_str());
  GURL merge_url(
      GaiaUrls::GetInstance()->merge_session_url().Resolve(merge_query));

  web_flow_ = CreateWebAuthFlow(merge_url);
  network::mojom::CookieManager* cookie_manager =
      web_flow_->GetGuestPartition()->GetCookieManagerForBrowserProcess();
  cookie_manager->DeleteCookies(
      network::mojom::CookieDeletionFilter::New(),
      base::BindOnce(&GaiaWebAuthFlow::OnCookiesDeleted,
                     weak_ptr_factory_.GetWeakPtr()));
}

void GaiaWebAuthFlow::OnCookiesDeleted(uint32_t num_deleted) {
  web_flow_->Start();
}

void GaiaWebAuthFlow::OnAuthFlowFailure(WebAuthFlow::Failure failure) {
  GaiaWebAuthFlow::Failure gaia_failure;

  switch (failure) {
    case WebAuthFlow::WINDOW_CLOSED:
      gaia_failure = GaiaWebAuthFlow::WINDOW_CLOSED;
      break;
    case WebAuthFlow::LOAD_FAILED:
      DVLOG(1) << "OnAuthFlowFailure LOAD_FAILED";
      gaia_failure = GaiaWebAuthFlow::LOAD_FAILED;
      break;
    default:
      NOTREACHED() << "Unexpected error from web auth flow: " << failure;
      gaia_failure = GaiaWebAuthFlow::LOAD_FAILED;
      break;
  }

  TRACE_EVENT_NESTABLE_ASYNC_INSTANT1("identity", "OnAuthFlowFailure", this,
                                      "error", gaia_failure);

  delegate_->OnGaiaFlowFailure(
      gaia_failure,
      GoogleServiceAuthError(GoogleServiceAuthError::NONE),
      std::string());
}

void GaiaWebAuthFlow::OnAuthFlowURLChange(const GURL& url) {
  TRACE_EVENT_NESTABLE_ASYNC_INSTANT0("identity", "OnAuthFlowURLChange", this);

  const char kOAuth2RedirectAccessTokenKey[] = "access_token";
  const char kOAuth2RedirectErrorKey[] = "error";
  const char kOAuth2ExpiresInKey[] = "expires_in";

  // The format of the target URL is:
  //     reversed.oauth.client.id:/extensionid#access_token=TOKEN

  if (url.scheme() == redirect_scheme_ && !url.has_host() && !url.has_port() &&
      base::StartsWith(url.GetContent(), redirect_path_prefix_,
                       base::CompareCase::SENSITIVE) &&
      url.has_ref()) {
    web_flow_.release()->DetachDelegateAndDelete();

    std::string fragment = url.ref();
    base::StringPairs pairs;
    base::SplitStringIntoKeyValuePairs(fragment, '=', '&', &pairs);
    std::string access_token;
    std::string error;
    std::string expiration;

    for (auto it = pairs.begin(); it != pairs.end(); ++it) {
      if (it->first == kOAuth2RedirectAccessTokenKey)
        access_token = it->second;
      else if (it->first == kOAuth2RedirectErrorKey)
        error = it->second;
      else if (it->first == kOAuth2ExpiresInKey)
        expiration = it->second;
    }

    if (access_token.empty() && error.empty()) {
      delegate_->OnGaiaFlowFailure(
          GaiaWebAuthFlow::INVALID_REDIRECT,
          GoogleServiceAuthError(GoogleServiceAuthError::NONE),
          std::string());
    } else if (!error.empty()) {
      delegate_->OnGaiaFlowFailure(
          GaiaWebAuthFlow::OAUTH_ERROR,
          GoogleServiceAuthError(GoogleServiceAuthError::NONE),
          error);
    } else {
      delegate_->OnGaiaFlowCompleted(access_token, expiration);
    }
  }
}

void GaiaWebAuthFlow::OnAuthFlowTitleChange(const std::string& title) {
  // On the final page the title will be "Loading <redirect-url>".
  // Treat it as though we'd really been redirected to <redirect-url>.
  const char kRedirectPrefix[] = "Loading ";
  std::string prefix(kRedirectPrefix);

  if (base::StartsWith(title, prefix, base::CompareCase::SENSITIVE)) {
    GURL url(title.substr(prefix.length(), std::string::npos));
    if (url.is_valid())
      OnAuthFlowURLChange(url);
  }
}

std::unique_ptr<WebAuthFlow> GaiaWebAuthFlow::CreateWebAuthFlow(GURL url) {
  return std::unique_ptr<WebAuthFlow>(
      new WebAuthFlow(this, profile_, url, WebAuthFlow::INTERACTIVE,
                      WebAuthFlow::GET_AUTH_TOKEN));
}

}  // namespace extensions