@Resolver((of) => Store)
export class StoresResolver {
  constructor(private readonly storesService: StoresService) {}

  @Query(returns => GetSidosOutput)
  getSidos(): Promise<GetSidosOutput> {
    return this.storesService.getSidos();
  }

  @Query(returns => GetGugunsOutput)
  getGuguns(): Promise<GetGugunsOutput> {
    return this.storesService.getGuguns();
  }

  @Query((returns) => GetStoresOutput)
  getStores(
    @Args('input') getStoresInput: GetStoresInput,
  ): Promise<GetStoresOutput> {
    return this.storesService.getStores(getStoresInput);
  }

  @Query((returns) => GetStoreOutput)
  getStore(
    @Args('input') getStoreInput: GetStoreInput,
  ): Promise<GetStoreOutput> {
    return this.storesService.getStore(getStoreInput);
  }

  @Mutation((returns) => CreateStoreOutput)
  createStore(
    @Args('input') createStoreInput: CreateStoreInput,
  ): Promise<CreateStoreOutput> {
    return this.storesService.createStore(createStoreInput);
  }

  @Mutation((returns) => UpdateStoreOutput)
  updateStore(
    @Args('input') updateStoreInput: UpdateStoreInput,
  ): Promise<UpdateStoreOutput> {
    return this.storesService.updateStore(updateStoreInput);
  }

  @Mutation((returns) => DeleteStoreOutput)
  deleteStore(
    @Args('input') deleteStoreInput: DeleteStoreInput,
  ): Promise<DeleteStoreOutput> {
    return this.storesService.deleteStore(deleteStoreInput);
  }
}


@Query(returns => GetSidosOutput)
  getSidos(): Promise<GetSidosOutput> {
    return this.storesService.getSidos();
  }


@Query(returns => GetGugunsOutput)
  getGuguns(): Promise<GetGugunsOutput> {
    return this.storesService.getGuguns();
  }


@Query((returns) => GetStoresOutput)
  getStores(
    @Args('input') getStoresInput: GetStoresInput,
  ): Promise<GetStoresOutput> {
    return this.storesService.getStores(getStoresInput);
  }


@Query((returns) => GetStoreOutput)
  getStore(
    @Args('input') getStoreInput: GetStoreInput,
  ): Promise<GetStoreOutput> {
    return this.storesService.getStore(getStoreInput);
  }


@Mutation((returns) => CreateStoreOutput)
  createStore(
    @Args('input') createStoreInput: CreateStoreInput,
  ): Promise<CreateStoreOutput> {
    return this.storesService.createStore(createStoreInput);
  }


@Mutation((returns) => UpdateStoreOutput)
  updateStore(
    @Args('input') updateStoreInput: UpdateStoreInput,
  ): Promise<UpdateStoreOutput> {
    return this.storesService.updateStore(updateStoreInput);
  }


@Mutation((returns) => DeleteStoreOutput)
  deleteStore(
    @Args('input') deleteStoreInput: DeleteStoreInput,
  ): Promise<DeleteStoreOutput> {
    return this.storesService.deleteStore(deleteStoreInput);
  }


data => {
      console.log(data);

      this.responseTest = data;
      this.listingTest = this.responseTest.response;
      this.file_url = this.responseTest.response[0].file_url;
    }


/**
 * Generated class for the TestsdetailsPage page.
 *
 * See https://ionicframework.com/docs/components/#navigation for more info on
 * Ionic pages and navigation.
 */
@IonicPage()
@Component({
  selector: 'page-testdetails',
  templateUrl: 'testdetails.html',
})
export class TestdetailsPage {
  responseTest: any = {
    "status": null,
    "error": null,
    "response": []
  };
  listingTest: [{}];
  file_url: string;

  constructor(public navCtrl: NavController,
    public navParams: NavParams,
    private viewCtrl: ViewController,
    private ms: MessageServiceProvider
  ) {
  }
  dismiss() {
    this.viewCtrl.dismiss();
  }

  beginTest(tid: any) {

    this.navCtrl.push('InstructionsPage', { test_id: tid, test_file: this.file_url });

  }

  ionViewDidLoad() {
    //console.log('ionViewDidLoad GroupdetailsPage');
    var tid = this.navParams.data.test_id;
    this.ms.getData("tests/" + tid).subscribe(data => {
      console.log(data);

      this.responseTest = data;
      this.listingTest = this.responseTest.response;
      this.file_url = this.responseTest.response[0].file_url;
    });

  }
}


dismiss() {
    this.viewCtrl.dismiss();
  }


beginTest(tid: any) {

    this.navCtrl.push('InstructionsPage', { test_id: tid, test_file: this.file_url });

  }


ionViewDidLoad() {
    //console.log('ionViewDidLoad GroupdetailsPage');
    var tid = this.navParams.data.test_id;
    this.ms.getData("tests/" + tid).subscribe(data => {
      console.log(data);

      this.responseTest = data;
      this.listingTest = this.responseTest.response;
      this.file_url = this.responseTest.response[0].file_url;
    });

  }


export class RulesProvider {
        private globalRules: Rules;
        private options: ts.FormatCodeSettings;
        private rulesMap: RulesMap;

        constructor() {
            this.globalRules = new Rules();
            const activeRules = this.globalRules.HighPriorityCommonRules.slice(0).concat(this.globalRules.UserConfigurableRules).concat(this.globalRules.LowPriorityCommonRules);
            this.rulesMap = RulesMap.create(activeRules);
        }

        public getRuleName(rule: Rule): string {
            return this.globalRules.getRuleName(rule);
        }

        public getRuleByName(name: string): Rule {
            return this.globalRules[name];
        }

        public getRulesMap() {
            return this.rulesMap;
        }

        public getFormatOptions(): Readonly<ts.FormatCodeSettings> {
            return this.options;
        }

        public ensureUpToDate(options: ts.FormatCodeSettings) {
            if (!this.options || !ts.compareDataObjects(this.options, options)) {
                this.options = ts.clone(options);
            }
        }
    }


public getRuleName(rule: Rule): string {
            return this.globalRules.getRuleName(rule);
        }


public getRuleByName(name: string): Rule {
            return this.globalRules[name];
        }


public getRulesMap() {
            return this.rulesMap;
        }


public getFormatOptions(): Readonly<ts.FormatCodeSettings> {
            return this.options;
        }


public ensureUpToDate(options: ts.FormatCodeSettings) {
            if (!this.options || !ts.compareDataObjects(this.options, options)) {
                this.options = ts.clone(options);
            }
        }


function getFaceDetectorOptions(net: faceapi.NeuralNetwork<any>) {
  return net === faceapi.nets.ssdMobilenetv1
    ? new faceapi.SsdMobilenetv1Options({ minConfidence })
    : (net === faceapi.nets.tinyFaceDetector
      ? new faceapi.TinyFaceDetectorOptions({ inputSize, scoreThreshold })
      : new faceapi.MtcnnOptions({ minFaceSize, scaleFactor })
    )
}


export function convertToDid (did: string, legacy: LegacyUport) {
  const publicKey: PublicKey[] = [{
    id: `${did}#keys-1`,
    type: 'Secp256k1VerificationKey2018',
    owner: did,
    publicKeyHex: legacy.publicKey.match(/^0x/) ? legacy.publicKey.slice(2) : legacy.publicKey
  }]

  if (legacy.publicEncKey) {
    publicKey.push({
      id: `${did}#keys-2`,
      type: 'Curve25519EncryptionPublicKey',
      owner: did,
      publicKeyBase64: legacy.publicEncKey
    })
  }

  const doc: DIDDocument = {
    '@context': 'https://w3id.org/did/v1',
    id: did,
    publicKey,
    authentication: [{
      type: 'Secp256k1SignatureAuthentication2018',
      publicKey: `${did}#keys-1`
    }]
  }

  if (legacy.name || legacy.description || legacy.image) {
    const profile = Object.assign({}, legacy)
    delete profile['publicKey']
    delete profile['publicEncKey']
    doc.uportProfile = profile
  }
  return doc
}


export default function register (configured?: UportLiteRegistry) {
  const cpsRegistry: UportLiteRegistry = configured || UportLite()

  const registry = (mnid: string) : Promise<LegacyUport | DIDDocument | null> => new Promise((resolve, reject) => {
    cpsRegistry(mnid, (error?: any, doc?: any) => error ? reject(error) : resolve(doc))
  })

  async function resolve (did: string, parsed: ParsedDID) : Promise<DIDDocument | null> {
    const doc = await registry(parsed.id)
    if (!doc) return null
    // Check if real DID document or legacy
    if ((<DIDDocument>doc)['@context'] === 'https://w3id.org/did/v1') return <DIDDocument>doc
    if (typeof doc['publicKey'] === 'string') {
      return convertToDid(did, <LegacyUport>doc)
    }
    return null
  }

  registerMethod('uport', resolve)
}


async function resolve (did: string, parsed: ParsedDID) : Promise<DIDDocument | null> {
    const doc = await registry(parsed.id)
    if (!doc) return null
    // Check if real DID document or legacy
    if ((<DIDDocument>doc)['@context'] === 'https://w3id.org/did/v1') return <DIDDocument>doc
    if (typeof doc['publicKey'] === 'string') {
      return convertToDid(did, <LegacyUport>doc)
    }
    return null
  }


(mnid: string) : Promise<LegacyUport | DIDDocument | null> => new Promise((resolve, reject) => {
    cpsRegistry(mnid, (error?: any, doc?: any) => error ? reject(error) : resolve(doc))
  })


(resolve, reject) => {
    cpsRegistry(mnid, (error?: any, doc?: any) => error ? reject(error) : resolve(doc))
  }


(error?: any, doc?: any) => error ? reject(error) : resolve(doc)


export declare interface LegacyUport {
  publicKey: string
  publicEncKey?: string
  name?: string
  description?: string
  image?: any
}


@Component({
  selector: 'app-confirm',
  templateUrl: './confirm.component.html',
  styleUrls: ['./confirm.component.css']
})

export class ConfirmComponent extends DialogComponent<ConfirmModel, boolean> implements ConfirmModel  {
  title: string
  message: string

  constructor(
    dialogService: DialogService
  ) {
    super(dialogService)
  }

  confirm() {
    // on click on confirm button we set dialog result as true,
    // ten we can get dialog result from caller code
    this.result = true;
    this.close();
  }
  cancel() {
    this.result = false;
    this.close();
  }
}


export interface ConfirmModel {
  title: string
  message: string
}


confirm() {
    // on click on confirm button we set dialog result as true,
    // ten we can get dialog result from caller code
    this.result = true;
    this.close();
  }


cancel() {
    this.result = false;
    this.close();
  }


(
  behandling: BehandlingAppKontekst,
  getKodeverkFn: (kodeverk: Kodeverk, behandlingType?: Kodeverk) => KodeverkMedNavn,
) => ({
  behandlingId: behandling.id,
  behandlingTypeNavn: getKodeverkFn(behandling.type, behandling.type).navn,
  behandlingTypeKode: behandling.type.kode,
  førsteÅrsak: behandling.førsteÅrsak,
  behandlendeEnhetId: behandling.behandlendeEnhetId,
  behandlendeEnhetNavn: behandling.behandlendeEnhetNavn,
  opprettetDato: behandling.opprettet,
  avsluttetDato: behandling.avsluttet,
  behandlingsstatus: getKodeverkFn(behandling.status, { kode: BehandlingType.FORSTEGANGSSOKNAD, kodeverk: '' }).navn,
  erGjeldendeVedtak: behandling.gjeldendeVedtak,
  behandlingsresultatTypeNavn: behandling.behandlingsresultat
    ? getKodeverkFn(behandling.behandlingsresultat.type, behandling.type).navn
    : undefined,
  behandlingsresultatTypeKode: behandling.behandlingsresultat ? behandling.behandlingsresultat.type.kode : undefined,
})


(
  behandling: BehandlingAppKontekst,
  getKodeverkFn: (kodeverk: Kodeverk, behandlingType?: Kodeverk) => KodeverkMedNavn,
  withChevronDown = false,
  withChevronUp = false,
): ReactElement => (
  <BehandlingPickerItemContentOld
    withChevronDown={withChevronDown}


(
  toggleShowAll: () => void,
  behandling: BehandlingAppKontekst,
  showAll: boolean,
  getKodeverkFn: (kodeverk: Kodeverk, behandlingType?: Kodeverk) => KodeverkMedNavn,
): ReactElement => (
  <button type="button"


(
  getBehandlingLocation: (behandlingId: number) => Location,
  behandling: BehandlingAppKontekst,
  isActive: boolean,
  toggleShowAll: () => void,
  showAll: boolean,
  getKodeverkFn: (kodeverk: Kodeverk, behandlingType?: Kodeverk) => KodeverkMedNavn,
): ReactElement => (
  <NavLink className={styles.linkToBehandling}


({
  onlyOneBehandling,
  behandling,
  getBehandlingLocation,
  isActive,
  showAll,
  toggleShowAll,
  getKodeverkFn,
}: OwnProps) => {
  if (onlyOneBehandling && isActive) {
    return renderItemContent(behandling, getKodeverkFn);
  }
  if (onlyOneBehandling || showAll) {
    return renderLinkToBehandling(getBehandlingLocation, behandling, isActive, toggleShowAll, showAll, getKodeverkFn);
  }
  if (isActive) {
    return renderToggleShowAllButton(toggleShowAll, behandling, showAll, getKodeverkFn);
  }
  return null;
}


interface OwnProps {
  onlyOneBehandling: boolean;
  behandling: BehandlingAppKontekst;
  getBehandlingLocation: (behandlingId: number) => Location;
  isActive: boolean;
  showAll: boolean;
  toggleShowAll: () => void;
  getKodeverkFn: (kodeverk: Kodeverk, behandlingType?: Kodeverk) => KodeverkMedNavn;
}


renderItemContent(behandling, getKodeverkFn, !showAll, showAll)


getBehandlingLocation(behandling


renderItemContent(behandling, getKodeverkFn, false, showAll


@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  title = 'astrology-is-bs';
}


() => (
  <Background color="bg-gray-100">
    <Section yPadding


@NgModule({
    imports: [
        CommonModule,
        DemoInputsRoutingModule,
        FormsModule,
        SharedModule,
    ],
    declarations: moduleDeclarations,
    exports: moduleDeclarations,
    entryComponents,
    providers: [],
    schemas: [
        CUSTOM_ELEMENTS_SCHEMA,
        NO_ERRORS_SCHEMA,
    ]
})
export class DemoInputsModule {
}


@Controller('search')
export class SearchController {
  constructor(private readonly searchService: SearchService) {}

  @Get()
  async search(
    @Query('terms') terms: string,
    @Query('resources') resources: string[]
  ): Promise<SearchResult[]> {
    return await this.searchService.search({
      terms,
      resources
    })
  }

  @Get('/get-search-result-objects')
  async getSearchResultObjects(
    @Query() resourcesToGet: { [key: string]: string | string[] }
  ): Promise<SearchResult[]> {
    return await this.searchService.getSearchResultObjects(resourcesToGet)
  }
}


@Get()
  async search(
    @Query('terms') terms: string,
    @Query('resources') resources: string[]
  ): Promise<SearchResult[]> {
    return await this.searchService.search({
      terms,
      resources
    })
  }


@Get('/get-search-result-objects')
  async getSearchResultObjects(
    @Query() resourcesToGet: { [key: string]: string | string[] }
  ): Promise<SearchResult[]> {
    return await this.searchService.getSearchResultObjects(resourcesToGet)
  }


() => {
        const characterList = document.getElementById(`character-list-${this.props.server.shardID}`);
        this.setState({ initialHeight: characterList.clientHeight - 40 });
      }


(result: SubscriptionResult<CharacterListSubscription.Subscription>) => {
    if (!result.data) return;

    const patcherAlertUpdate = result.data.patcherAlerts;
    if (!patcherAlertUpdate) return;

    const patcherAlerts = [...this.props.patcherAlerts];
    patcherAlerts.push(patcherAlertUpdate.alert);
    this.props.onUpdateState({ patcherAlerts });
  }


() => {
    setTimeout(() => {
      const characterList = document.getElementById(`character-list-${this.props.server.shardID}`);
      if (!this.state.initialHeight) {
        this.setState({ initialHeight: characterList.clientHeight - 40 });
      }
    }, 300);
  }


() => {
      const characterList = document.getElementById(`character-list-${this.props.server.shardID}`);
      if (!this.state.initialHeight) {
        this.setState({ initialHeight: characterList.clientHeight - 40 });
      }
    }


(e: React.MouseEvent<HTMLDivElement>, server: PatcherServer) => {
    game.trigger('play-sound', 'select');
    this.props.toggleMenu(e, server);
  }


(collapsed: boolean) => {
    game.trigger('play-sound', 'select-change');

    this.props.onToggleCollapse(this.props.server.shardID, collapsed);
  }


(collapsed: boolean) => {
    if (!collapsed) {
      return {
        anim: css`
          opacity: 1;
          visibility: visible;
          height: ${this.state.initialHeight ? `${this.state.initialHeight}px` : '100%'};
          overflow: hidden;
        `,
      };
    } else {
      return {
        anim: css`
          opacity: 0;
          visibility: hidden;
          height: 0px;
          overflow: hidden;
        `,
      };
    }
  }


class CharacterList extends React.PureComponent<Props, CharacterListState> {
  constructor(props: Props) {
    super(props);
    this.state = {
      initialHeight: null,
    };
  }
  public render() {
    const { server, serverCharacters, sortedServers, index } = this.props;
    return (
      <div id={`character-list-${server.shardID}`} style={


class CharacterListWithInjectedContext extends React.Component<ComponentProps> {
  public render() {
    return (
      <ControllerContext.Consumer>
        {({ servers, characters, patcherAlerts, onUpdateState }) => (
          <CharacterList
            {...this.props}
            servers={servers}
            characters={characters}
            patcherAlerts={patcherAlerts}
            onUpdateState={onUpdateState}
          />
        )}


export interface ComponentProps {
  index: number;
  collapsed: boolean;
  server: PatcherServer;
  sortedServers: PatcherServer[];
  serverCharacters: SimpleCharacter[];
  selectedCharacter: SimpleCharacter;
  onCharacterSelect: (character: SimpleCharacter) => void;
  onChooseCharacter: (character: SimpleCharacter) => void;
  toggleMenu: (e: React.MouseEvent<HTMLDivElement>, server: PatcherServer) => void;
  onToggleCollapse: (shardID: number, collapsed: boolean) => void;
  charSelectVisible: boolean;
  apiServerOnline: 'Online' | 'Offline' | undefined;
}


export interface InjectedProps {
  servers: {[id: string]: PatcherServer};
  characters: {[id: string]: SimpleCharacter};
  patcherAlerts: PatcherAlert[];
  onUpdateState: (stats: Partial<ContextState>) => void;
}


export interface CharacterListState {
  initialHeight: number;
}


export type Props = ComponentProps & InjectedProps;


public render() {
    const { server, serverCharacters, sortedServers, index } = this.props;
    return (
      <div id={`character-list-${server.shardID}`}


public render() {
    return (
      <ControllerContext.Consumer>
        {({ servers, characters, patcherAlerts, onUpdateState }) => (
          <CharacterList
            {...this.props}
            servers={servers}
            characters={characters}
            patcherAlerts={patcherAlerts}
            onUpdateState={onUpdateState}
          />
        )}


(props: DaimleyTheme) => props.theme.backgroundGray


(props: DaimleyTheme) => props.theme.underlineGreen


(props: DaimleyTheme) => props.theme?.orange


export class PathAddressContractAddress extends PathAddress {

    @IsNotEmpty()
    @Length(42, 42)
    public contractAddress: string;
}


interface MenuConfig {
    name: string;
    link: string;
    active?: string[];
    sub?: MenuConfig[];
}


export declare class CtrInput {
    private completer;
    private ngModel;
    clearSelected: boolean;
    overrideSuggested: boolean;
    fillHighlighted: boolean;
    ngModelChange: EventEmitter<any>;
    private _searchStr;
    private _displayStr;
    constructor(completer: CtrCompleter, ngModel: NgModel);
    keyupHandler(event: any): void;
    keydownHandler(event: any): void;
    onBlur(event: any): void;
    searchStr: string;
    private handleSelection();
    private restoreSearchValue();
}


keyupHandler(event: any): void;


keydownHandler(event: any): void;


onBlur(event: any): void;


private handleSelection();


private restoreSearchValue();


value => {
          if (value.workflow === me.applicationType && (value.action === 'register' || value.action === 'unregister'))
            me.refreshList();
        }


value => {
          if (value && value.action === 'activate-view')
            me.view = value.target as NvPairModel;
        }


sub => {
      sub.unsubscribe();
    }


@Component({
  selector: 'oracle-inventory',
  templateUrl: './oracle-inventory.component.html',
  styleUrls: ['./oracle-inventory.component.scss'],
})
export class OracleInventoryComponent {
  @Output() itemSelect = new EventEmitter<AppServerModel>();
  @Output() itemEdit = new EventEmitter<AppServerModel>();
  @Output() assignPolicy = new EventEmitter<BaseApplicationModel>();
  @Output() editRunSettings = new EventEmitter<BaseApplicationModel>();
  @ViewChild(ApplicationListComponent) list: ApplicationListComponent;
  @ViewChild(SqlStatChartComponent) chart: SqlStatChartComponent;

  private applicationType: string = 'oracle';
  private subscriptions: Subscription[] = [];
  private view: NvPairModel;
  private selectedCategory: ApplicationInventoryCategory;

  get inCategoryView(): boolean {
    return !!this.selectedCategory;
  }

  constructor(private inventorySvc: ApplicationInventoryService) {
  }

  ngOnInit(): void {
    let me = this;
    me.subscriptions.push(
      ...me.inventorySvc.subscribe<ApplicationRegistrationSubjectModel>('registration',
        undefined,
        value => {
          if (value.workflow === me.applicationType && (value.action === 'register' || value.action === 'unregister'))
            me.refreshList();
        }),
      ...me.inventorySvc.subscribe<ApplicationStatSubjectModel>('stat',
        me.applicationType as ApplicationInventoryWorkflow,
        value => {
          if (value && value.action === 'activate-view')
            me.view = value.target as NvPairModel;
        })
    );
  }

  ngOnDestroy(): void {
    this.subscriptions.forEach(sub => {
      sub.unsubscribe();
    });
  }

  refreshList(): void {
    this.list.refresh();
  }

  onItemSelect(item: AppServerModel): void {
    this.itemSelect.emit(item);
  }

  onItemEdit(item: AppServerModel): void {
    this.itemEdit.emit(item);
  }

  onStatCategorySelect(item: InventoryDoughnutChartData): void {
    this.selectedCategory = item ? new ApplicationInventoryCategory(
      item.label as ApplicationInventoryCategoryLabel,
      item.value,
      this.applicationType) : undefined;
  }

  onCategoryViewClose(): void {
    this.selectedCategory = undefined;
    if (this.chart)
      this.chart.deselect();
  }

  onAssignPolicy(item: BaseApplicationModel): void {
    this.assignPolicy.emit(item);
  }

  onEditRunSettings(item: BaseApplicationModel): void {
    this.editRunSettings.emit(item);
  }
}


ngOnInit(): void {
    let me = this;
    me.subscriptions.push(
      ...me.inventorySvc.subscribe<ApplicationRegistrationSubjectModel>('registration',
        undefined,
        value => {
          if (value.workflow === me.applicationType && (value.action === 'register' || value.action === 'unregister'))
            me.refreshList();
        }),
      ...me.inventorySvc.subscribe<ApplicationStatSubjectModel>('stat',
        me.applicationType as ApplicationInventoryWorkflow,
        value => {
          if (value && value.action === 'activate-view')
            me.view = value.target as NvPairModel;
        })
    );
  }


ngOnDestroy(): void {
    this.subscriptions.forEach(sub => {
      sub.unsubscribe();
    });
  }


refreshList(): void {
    this.list.refresh();
  }


onItemSelect(item: AppServerModel): void {
    this.itemSelect.emit(item);
  }


onItemEdit(item: AppServerModel): void {
    this.itemEdit.emit(item);
  }


onStatCategorySelect(item: InventoryDoughnutChartData): void {
    this.selectedCategory = item ? new ApplicationInventoryCategory(
      item.label as ApplicationInventoryCategoryLabel,
      item.value,
      this.applicationType) : undefined;
  }


onCategoryViewClose(): void {
    this.selectedCategory = undefined;
    if (this.chart)
      this.chart.deselect();
  }


onAssignPolicy(item: BaseApplicationModel): void {
    this.assignPolicy.emit(item);
  }


onEditRunSettings(item: BaseApplicationModel): void {
    this.editRunSettings.emit(item);
  }


() => {
  /* eslint-disable react/jsx-max-depth */
  test('should render the version page', async () => {
    const { getByTestId, getByText } = render(
      <MemoryRouter>
        <DetailContext.Provider value={detailContextValue}>
          <Version />
        </DetailContext.Provider>
      </MemoryRouter>
    );
    // we wait fetch response (mocked above)
    await waitForElement(() => getByTestId('version-layout'));
    // check whether readme was loaded
    const hasReadme = getByText(detailContextValue.readMe);
    expect(hasReadme).toBeTruthy();
  });

  test('should render 404 page if the resources are not found', async () => {
    const { getByText } = render(
      <MemoryRouter>
        <DetailContext.Provider
          value={{
            ...detailContextValue,
            hasNotBeenFound: true,
          }}>
          <Version />
        </DetailContext.Provider>
      </MemoryRouter>
    );
    // we wait fetch response (mocked above)
    const notFoundElement = await waitForElement(() => getByText(NOT_FOUND_TEXT));
    expect(notFoundElement).toBeTruthy();
  }


async () => {
    const { getByTestId, getByText } = render(
      <MemoryRouter>
        <DetailContext.Provider value={detailContextValue}>
          <Version />
        </DetailContext.Provider>
      </MemoryRouter>
    );
    // we wait fetch response (mocked above)
    await waitForElement(() => getByTestId('version-layout'));
    // check whether readme was loaded
    const hasReadme = getByText(detailContextValue.readMe);
    expect(hasReadme).toBeTruthy();
  }


() => getByTestId('version-layout')


async () => {
    const { getByText } = render(
      <MemoryRouter>
        <DetailContext.Provider
          value={{
            ...detailContextValue,
            hasNotBeenFound: true,
          }}


() => getByText(NOT_FOUND_TEXT)


async (
  transport: Transport,
  appId: string,
  appPrivateKey: string,
  coinType: string,
  changeAddressIndex: number,
  redeemType: string
) => {
  const changeKeyId = addressIndexToKeyId(coinType, changeAddressIndex);
  const sig = await getCommandSignature(
    transport,
    appId,
    appPrivateKey,
    commands.SET_CHANGE_KEYID,
    changeKeyId,
    redeemType
  );
  const keyWithSig = changeKeyId + sig.signature;
  const { statusCode, msg } = await executeCommand(transport, commands.SET_CHANGE_KEYID, target.SE, keyWithSig, redeemType);
  if (statusCode !== CODE._9000) {
    throw new APDUError(commands.SET_CHANGE_KEYID, statusCode, msg)
  }
}


async (
  transport: Transport,
  txDataHex: string,
  txDataType: string,
  appPrivateKey: string
): Promise<string> => {

  let encryptedSignature;
  let statusCode = CODE._9999;
  let msg = 'prepareTx get encryptedSignature failed';
  if (txDataType != '00') {
    const result = await executeCommand(transport, commands.TX_PREPARE, target.SE, txDataHex, txDataType, '00');
    statusCode = result.statusCode
    msg = result.msg
    if (statusCode === CODE._9000) {
      return statusCode;
    } else {
      throw new APDUError(commands.TX_PREPARE, statusCode, msg)
    }
  } else {
    const sig = await getCommandSignatureWithoutNonce(
      transport,
      appPrivateKey,
      commands.TX_PREPARE,
      txDataHex,
      txDataType,
      '00'
    );
    const sendData = txDataHex + sig;
    const patch = Math.ceil(sendData.length / 500);
    for (let i = 0; i < patch; i++) {
      const patchData = sendData.substr(i * 500, 500);
      const p2 = patch === 1 ? "00" : (i === patch - 1 ? "8" : "0") + (i + 1);
      // eslint-disable-next-line no-await-in-loop
      const result = await executeCommand(transport, commands.TX_PREPARE, target.SE, patchData, txDataType, p2);

      if (i == patch - 1) {
        encryptedSignature = result.outputData;
        statusCode = result.statusCode
        msg = result.msg
      }
    }
    if (encryptedSignature) {
      return encryptedSignature;
    } else {
      throw new APDUError(commands.TX_PREPARE, statusCode, msg)
    }
  }
}


async (transport: Transport, script: string) => {
  const { statusCode, msg } = await executeCommand(
    transport,
    commands.SEND_SCRIPT,
    target.SE,
    script,
    undefined,
    undefined,
    false
  );
  if (statusCode === CODE._9000) {
    return true;
  } else {
    throw new APDUError(commands.SEND_SCRIPT, statusCode, msg)
  }
}


async (
  transport: Transport,
  appId: string,
  appPrivKey: string,
  argument: string,
) => {
  const { signature } = await getCommandSignature(
    transport,
    appId,
    appPrivKey,
    commands.EXECUTE_SCRIPT,
    argument,
    undefined,
    undefined
  );
  const { outputData: encryptedSignature, statusCode, msg } = await executeCommand(
    transport,
    commands.EXECUTE_SCRIPT,
    target.SE,
    argument + signature,
    undefined,
    undefined,
    true,
  );
  if (encryptedSignature) {
    return encryptedSignature;
  } else {
    throw new APDUError(commands.EXECUTE_SCRIPT, statusCode, msg)
  }
}


async (
  transport: Transport,
  appId: string,
  appPrivKey: string,
  utxoArgument: string,
  extraTransactionType: string
) => {
  const { signature } = await getCommandSignature(
    transport,
    appId,
    appPrivKey,
    commands.EXECUTE_UTXO_SCRIPT,
    utxoArgument,
    extraTransactionType,
    undefined
  );
  const { outputData: encryptedSignature, statusCode, msg } = await executeCommand(
    transport,
    commands.EXECUTE_UTXO_SCRIPT,
    target.SE,
    utxoArgument + signature,
    extraTransactionType,
    undefined,
    true,
  );
  if (encryptedSignature) {
    return encryptedSignature;
  } else {
    throw new APDUError(commands.EXECUTE_UTXO_SCRIPT, statusCode, msg)
  }
}


async (transport: Transport): Promise<{ signedTx: string, statusCode: string }> => {
  const { outputData: signedTx, statusCode, msg } = await executeCommand(transport, commands.GET_SIGNED_HEX, target.SE);
  if (statusCode === CODE._9000 || statusCode === CODE._6D00) {
    return { signedTx, statusCode };
  } else {
    throw new APDUError(commands.GET_SIGNED_HEX, statusCode, msg)
  }
}


async (transport: Transport): Promise<boolean> => {
  const { statusCode, msg } = await executeCommand(transport, commands.FINISH_PREPARE, target.SE);
  if (statusCode === CODE._9000) {
    return true;
  } else {
    throw new APDUError(commands.FINISH_PREPARE, statusCode, msg)
  }
}


async (transport: Transport): Promise<string> => {
  const { outputData: signatureKey, statusCode, msg } = await executeCommand(transport, commands.GET_TX_KEY, target.SE);
  if (signatureKey) {
    return signatureKey;
  } else {
    throw new APDUError(commands.GET_TX_KEY, statusCode, msg)
  }
}


async (transport: Transport): Promise<boolean> => {
  const { statusCode, msg } = await executeCommand(transport, commands.CLEAR_TX, target.SE);
  if (statusCode === CODE._9000) {
    return true;
  } else {
    throw new APDUError(commands.CLEAR_TX, statusCode, msg)
  }
}


async (transport: Transport): Promise<boolean> => {
  const { statusCode, msg } = await executeCommand(transport, commands.GET_TX_DETAIL, target.SE);
  if (statusCode === CODE._9000) {
    return true;
  } else {
    throw new APDUError(commands.GET_TX_DETAIL, statusCode, msg)
  }
}


async (transport: Transport, payload: string, sn: number = 1): Promise<boolean> => {
  const command = sn === 1 ? commands.SET_ERC20_TOKEN : commands.SET_SECOND_ERC20_TOKEN;
  const { statusCode, msg } = await executeCommand(transport, command, target.SE, payload)
  if (statusCode === CODE._9000) {
    return true;
  } else {
    throw new APDUError(command, statusCode, msg)
  }
}


async (transport: Transport, payload: string, sn: number = 1): Promise<boolean> => {
  const command = sn === 1 ? commands.SET_ERC20_TOKEN : commands.SET_SECOND_ERC20_TOKEN;
  const { statusCode, msg } = await executeCommand(transport, command, target.SE, payload, '04', '18')
  if (statusCode === CODE._9000) {
    return true;
  } else {
    throw new APDUError(command, statusCode, msg)
  }
}


() => {
  const [juryProject, setJuryProject] = useState<JuryStore | null>(null);

  const { isFetched, isLoading, items } = useValues(juriesLogic);
  const { getItems } = useActions(juriesLogic);

  const { data } = useValues(authLogic);

  useEffect(() => {
    if (items.length > 0) {
      const jury = items.filter((j) => j.usuario.codRegistro === data.username);
      if (jury.length > 0) {
        setJuryProject(jury[0]);
      }
    }
  }, [items]);

  useEffect(() => {
    if (!isFetched) getItems();
  }, [isFetched]);

  if (isLoading) return <LoaderWithText text="Obteniendo su lista de proyectos..." />;

  if ((isFetched && items.length === 0) || !juryProject)
    return <NoItemsText text="Ups! parece que no le han asignado ningún proyecto." />;

  if (!isFetched) return <NoItemsText error text="Error obteniendo datos..." />;

  return (
    <div>
      <RateForm projects={juryProject.proyectos.map((p) => p.proyecto)} />
      <Title text="Proyectos" />

      <JuryCard jury={juryProject} />
    </div>


() => {
    if (items.length > 0) {
      const jury = items.filter((j) => j.usuario.codRegistro === data.username);
      if (jury.length > 0) {
        setJuryProject(jury[0]);
      }
    }
  }


(j) => j.usuario.codRegistro === data.username


() => {
    if (!isFetched) getItems();
  }


(p) => p.proyecto


async () => {
  mongoServer = new MongoMemoryServer();
  mongoUri = await mongoServer.getConnectionString();
  server = await startServer(mongoUri);
}


async () => {
  await closeServer(server, mongoUri);
  await mongoServer.stop();
}


() => {
  test('should return 401 without valid authorization', async () => {
    const response = await request(server).post('/api/assignment');
    expect(response.status).toEqual(401);
  });
}


async () => {
    const response = await request(server).post('/api/assignment');
    expect(response.status).toEqual(401);
  }


(data: { savingsTransactionReciept: any }) => {
      this.transactionRecieptData = data.savingsTransactionReciept;
    }


/**
 * View Transaction Reciept Component
 */
@Component({
  selector: 'mifosx-view-reciept',
  templateUrl: './view-reciept.component.html',
  styleUrls: ['./view-reciept.component.scss']
})
export class ViewRecieptComponent implements OnInit {

  /** trusted resource url for pentaho output */
  pentahoUrl: any;
  /** Transaction Reciept Data */
  transactionRecieptData: any;

  /**
   * Fetches transaction reciept `resolve`
   * @param {DomSanitizer} sanitizer DOM Sanitizer
   * @param {ActivatedRoute} route Activated Route
   */
  constructor(private sanitizer: DomSanitizer,
              private route: ActivatedRoute) {
    this.route.data.subscribe((data: { savingsTransactionReciept: any }) => {
      this.transactionRecieptData = data.savingsTransactionReciept;
    });
  }

  ngOnInit() {
    const contentType = this.transactionRecieptData.headers.get('Content-Type');
    const file = new Blob([this.transactionRecieptData.body], {type: contentType});
    const filecontent = URL.createObjectURL(file);
    this.pentahoUrl = this.sanitizer.bypassSecurityTrustResourceUrl(filecontent);
  }

}


ngOnInit() {
    const contentType = this.transactionRecieptData.headers.get('Content-Type');
    const file = new Blob([this.transactionRecieptData.body], {type: contentType});
    const filecontent = URL.createObjectURL(file);
    this.pentahoUrl = this.sanitizer.bypassSecurityTrustResourceUrl(filecontent);
  }


/**
 *
Gets information about a test.

 * @example
 * Use a bare-bones client and the command you need to make an API call.
 * ```javascript
 * import { DeviceFarmClient, GetTestCommand } from "../../client-device-farm/mod.ts";
 * // const { DeviceFarmClient, GetTestCommand } = require("@aws-sdk/client-device-farm"); // CommonJS import
 * const client = new DeviceFarmClient(config);
 * const command = new GetTestCommand(input);
 * const response = await client.send(command);
 * ```
 *
 * @see {@link GetTestCommandInput} for command's `input` shape.
 * @see {@link GetTestCommandOutput} for command's `response` shape.
 * @see {@link DeviceFarmClientResolvedConfig | config} for command's `input` shape.
 *
 */
export class GetTestCommand extends $Command<
  GetTestCommandInput,
  GetTestCommandOutput,
  DeviceFarmClientResolvedConfig
> {
  // Start section: command_properties
  // End section: command_properties

  constructor(readonly input: GetTestCommandInput) {
    // Start section: command_constructor
    super();
    // End section: command_constructor
  }

  /**
   * @internal
   */
  resolveMiddleware(
    clientStack: MiddlewareStack<ServiceInputTypes, ServiceOutputTypes>,
    configuration: DeviceFarmClientResolvedConfig,
    options?: __HttpHandlerOptions
  ): Handler<GetTestCommandInput, GetTestCommandOutput> {
    this.middlewareStack.use(getSerdePlugin(configuration, this.serialize, this.deserialize));

    const stack = clientStack.concat(this.middlewareStack);

    const { logger } = configuration;
    const clientName = "DeviceFarmClient";
    const commandName = "GetTestCommand";
    const handlerExecutionContext: HandlerExecutionContext = {
      logger,
      clientName,
      commandName,
      inputFilterSensitiveLog: GetTestRequest.filterSensitiveLog,
      outputFilterSensitiveLog: GetTestResult.filterSensitiveLog,
    };
    const { requestHandler } = configuration;
    return stack.resolve(
      (request: FinalizeHandlerArguments<any>) =>
        requestHandler.handle(request.request as __HttpRequest, options || {}),
      handlerExecutionContext
    );
  }

  private serialize(input: GetTestCommandInput, context: __SerdeContext): Promise<__HttpRequest> {
    return serializeAws_json1_1GetTestCommand(input, context);
  }

  private deserialize(output: __HttpResponse, context: __SerdeContext): Promise<GetTestCommandOutput> {
    return deserializeAws_json1_1GetTestCommand(output, context);
  }

  // Start section: command_body_extra
  // End section: command_body_extra
}


export interface GetTestCommandInput extends GetTestRequest {}


export interface GetTestCommandOutput extends GetTestResult, __MetadataBearer {}


/**
   * @internal
   */
resolveMiddleware(
    clientStack: MiddlewareStack<ServiceInputTypes, ServiceOutputTypes>,
    configuration: DeviceFarmClientResolvedConfig,
    options?: __HttpHandlerOptions
  ): Handler<GetTestCommandInput, GetTestCommandOutput> {
    this.middlewareStack.use(getSerdePlugin(configuration, this.serialize, this.deserialize));

    const stack = clientStack.concat(this.middlewareStack);

    const { logger } = configuration;
    const clientName = "DeviceFarmClient";
    const commandName = "GetTestCommand";
    const handlerExecutionContext: HandlerExecutionContext = {
      logger,
      clientName,
      commandName,
      inputFilterSensitiveLog: GetTestRequest.filterSensitiveLog,
      outputFilterSensitiveLog: GetTestResult.filterSensitiveLog,
    };
    const { requestHandler } = configuration;
    return stack.resolve(
      (request: FinalizeHandlerArguments<any>) =>
        requestHandler.handle(request.request as __HttpRequest, options || {}),
      handlerExecutionContext
    );
  }


private serialize(input: GetTestCommandInput, context: __SerdeContext): Promise<__HttpRequest> {
    return serializeAws_json1_1GetTestCommand(input, context);
  }


private deserialize(output: __HttpResponse, context: __SerdeContext): Promise<GetTestCommandOutput> {
    return deserializeAws_json1_1GetTestCommand(output, context);
  }


function distance(a: Point2D, b: Point2D): number {
  return Math.sqrt(Math.pow(a[0] - b[0], 2) + Math.pow(a[1] - b[1], 2));
}


export type CrudOptionsInterface = Pick<CrudOptions, 'model' | 'dto'>;


export class BaseGenerator extends Generator {
  paths () {
    const templatePath = path.resolve(__dirname, '../../../templates')
    this.sourceRoot(templatePath)
  }
}


paths () {
    const templatePath = path.resolve(__dirname, '../../../templates')
    this.sourceRoot(templatePath)
  }


() => {
  return (
    <>
      <Upload />
    </>
  );
}


() => {
    page = new CkfrontendPage();
  }

_/Angular 2 App & Clients Component/e2e/app.e2e-spec.ts


() => {
    page.navigateTo();
    expect(page.getParagraphText()).toEqual('app works!');
  }


async <T>(key: string, resolver: () => Promise<T>, refresh = false): Promise<T> => {
  if (!store[key] || refresh) {
    const value = await resolver();
    store[key] = value;
  }
  return store[key];
}


(keysStartingWith: string): void => {
  const keys = Object.keys(store);

  for (const key of keys) {
    if (key.startsWith(keysStartingWith)) {
      delete store[key];
    }
  }
}


() => {
	test("when called with '' it returns the root node with no children", () => {
		const root = parseTrie("");

		expectRoot({root, childrenSize: 0});
	});

	test("when called with 'a' it returns the root node with 'a' as the only child", () => {
		const root = parseTrie("a");
		const a = root.children.get("a")!;

		expectRoot({root, childrenSize: 1});
		expectChild({child: a, parent: root, domain: "a", childrenSize: 0});
	});

	test("when called with 'a,b' it adds both as children of root", () => {
		const root = parseTrie("a,b");
		const a = root.children.get("a")!;
		const b = root.children.get("b")!;

		expectRoot({root, childrenSize: 2});
		expectChild({child: a, parent: root, domain: "a", childrenSize: 0});
		expectChild({child: b, parent: root, domain: "b", childrenSize: 0});
	});

	test("when called with 'a>b' it adds 'b' as child of 'a'", () => {
		const root = parseTrie("a>b");
		const a = root.children.get("a")!;
		const b = a.children.get("b")!;

		expectRoot({root, childrenSize: 1});
		expectChild({child: a, parent: root, domain: "a", childrenSize: 1});
		expectChild({child: b, parent: a, domain: "b", childrenSize: 0});
	});

	test("when called with 'a>b,c' it adds 'b' and 'c' as child of 'a'", () => {
		const root = parseTrie("a>b,c");
		const a = root.children.get("a")!;
		const b = a.children.get("b")!;
		const c = a.children.get("c")!;

		expectRoot({root, childrenSize: 1});
		expectChild({child: a, parent: root, domain: "a", childrenSize: 2});
		expectChild({child: b, parent: a, domain: "b", childrenSize: 0});
		expectChild({child: c, parent: a, domain: "c", childrenSize: 0});
	});

	test("when called with 'a>b<c' it adds 'b' as child of 'a' and 'c' as child of root", () => {
		const root = parseTrie("a>b<c");
		const a = root.children.get("a")!;
		const b = a.children.get("b")!;
		const c = root.children.get("c")!;

		expectRoot({root, childrenSize: 2});
		expectChild({child: a, parent: root, domain: "a", childrenSize: 1});
		expectChild({child: b, parent: a, domain: "b", childrenSize: 0});
		expectChild({child: c, parent: root, domain: "c", childrenSize: 0});
	});

	test("when called with 'a|b' it adds 'a' and 'b' as child of root", () => {
		const root = parseTrie("a|b");
		const a = root.children.get("a")!;
		const b = root.children.get("b")!;

		expectRoot({root, childrenSize: 2});
		expectChild({child: a, parent: root, domain: "a", childrenSize: 0});
		expectChild({child: b, parent: root, domain: "b", childrenSize: 0});
	});

	test("when called with 'a>b|c' it adds 'b' as child of 'a' and 'c' as child of root", () => {
		const root = parseTrie("a>b|c");
		const a = root.children.get("a")!;
		const b = a.children.get("b")!;
		const c = root.children.get("c")!;

		expectRoot({root, childrenSize: 2});
		expectChild({child: a, parent: root, domain: "a", childrenSize: 1});
		expectChild({child: b, parent: a, domain: "b", childrenSize: 0});
		expectChild({child: c, parent: root, domain: "c", childrenSize: 0});
	});

	test("when called with 'a>bd' it adds 'b' and 'c' as child of 'a'", () => {
		const root = parseTrie("a>bc");
		const a = root.children.get("a")!;
		const b = a.children.get("b")!;
		const c = a.children.get("c")!;

		expectRoot({root, childrenSize: 1});
		expectChild({child: a, parent: root, domain: "a", childrenSize: 2});
		expectChild({child: b, parent: a, domain: "b", childrenSize: 0});
		expectChild({child: c, parent: a, domain: "c", childrenSize: 0});
	});

	test("when called with 'a>b>c' it adds 'c' as child of 'b'", () => {
		const root = parseTrie("a>b>c");
		const a = root.children.get("a")!;
		const b = a.children.get("b")!;
		const c = b.children.get("c")!;

		expectRoot({root, childrenSize: 1});
		expectChild({child: a, parent: root, domain: "a", childrenSize: 1});
		expectChild({child: b, parent: a, domain: "b", childrenSize: 1});
		expectChild({child: c, parent: b, domain: "c", childrenSize: 0});
	});

	test("when called with 'a>b>c>d|e' it adds 'e' as child node of root", () => {
		const root = parseTrie("a>b>c>d|e");
		const e = root.children.get("e")!;

		expectRoot({root, childrenSize: 2});
		expectChild({child: e, parent: root, domain: "e", childrenSize: 0});
	});

	test("when called with '<' it throws an error with a helpful error message", () => {
		expect(() => parseTrie("<")).toThrowErrorMatchingInlineSnapshot(
			`"Error in serialized trie at position 0: Cannot go up, current parent node is already root"`,
		);
	});
}


() => {
		const root = parseTrie("");

		expectRoot({root, childrenSize: 0});
	}


() => {
		const root = parseTrie("a");
		const a = root.children.get("a")!;

		expectRoot({root, childrenSize: 1});
		expectChild({child: a, parent: root, domain: "a", childrenSize: 0});
	}


() => {
		const root = parseTrie("a,b");
		const a = root.children.get("a")!;
		const b = root.children.get("b")!;

		expectRoot({root, childrenSize: 2});
		expectChild({child: a, parent: root, domain: "a", childrenSize: 0});
		expectChild({child: b, parent: root, domain: "b", childrenSize: 0});
	}


() => {
		const root = parseTrie("a>b");
		const a = root.children.get("a")!;
		const b = a.children.get("b")!;

		expectRoot({root, childrenSize: 1});
		expectChild({child: a, parent: root, domain: "a", childrenSize: 1});
		expectChild({child: b, parent: a, domain: "b", childrenSize: 0});
	}


() => {
		const root = parseTrie("a>b,c");
		const a = root.children.get("a")!;
		const b = a.children.get("b")!;
		const c = a.children.get("c")!;

		expectRoot({root, childrenSize: 1});
		expectChild({child: a, parent: root, domain: "a", childrenSize: 2});
		expectChild({child: b, parent: a, domain: "b", childrenSize: 0});
		expectChild({child: c, parent: a, domain: "c", childrenSize: 0});
	}


() => {
		const root = parseTrie("a>b<c");
		const a = root.children.get("a")!;
		const b = a.children.get("b")!;
		const c = root.children.get("c")!;

		expectRoot({root, childrenSize: 2});
		expectChild({child: a, parent: root, domain: "a", childrenSize: 1});
		expectChild({child: b, parent: a, domain: "b", childrenSize: 0});
		expectChild({child: c, parent: root, domain: "c", childrenSize: 0});
	}


() => {
		const root = parseTrie("a|b");
		const a = root.children.get("a")!;
		const b = root.children.get("b")!;

		expectRoot({root, childrenSize: 2});
		expectChild({child: a, parent: root, domain: "a", childrenSize: 0});
		expectChild({child: b, parent: root, domain: "b", childrenSize: 0});
	}


() => {
		const root = parseTrie("a>b|c");
		const a = root.children.get("a")!;
		const b = a.children.get("b")!;
		const c = root.children.get("c")!;

		expectRoot({root, childrenSize: 2});
		expectChild({child: a, parent: root, domain: "a", childrenSize: 1});
		expectChild({child: b, parent: a, domain: "b", childrenSize: 0});
		expectChild({child: c, parent: root, domain: "c", childrenSize: 0});
	}


() => {
		const root = parseTrie("a>b<a>c");
		const a = root.children.get("a")!;
		const b = a.children.get("b")!;
		const c = a.children.get("c")!;

		expectRoot({root, childrenSize: 1});
		expectChild({child: a, parent: root, domain: "a", childrenSize: 2});
		expectChild({child: b, parent: a, domain: "b", childrenSize: 0});
		expectChild({child: c, parent: a, domain: "c", childrenSize: 0});
	}


() => {
		const root = parseTrie("a>b>c");
		const a = root.children.get("a")!;
		const b = a.children.get("b")!;
		const c = b.children.get("c")!;

		expectRoot({root, childrenSize: 1});
		expectChild({child: a, parent: root, domain: "a", childrenSize: 1});
		expectChild({child: b, parent: a, domain: "b", childrenSize: 1});
		expectChild({child: c, parent: b, domain: "c", childrenSize: 0});
	}


() => {
		const root = parseTrie("a>b>c>d|e");
		const e = root.children.get("e")!;

		expectRoot({root, childrenSize: 2});
		expectChild({child: e, parent: root, domain: "e", childrenSize: 0});
	}


() => {
		expect(() => parseTrie("<")).toThrowErrorMatchingInlineSnapshot(
			`"Error in serialized trie at position 0: Cannot go up, current parent node is already root"`,
		);
	}


() => parseTrie("<")


export function MapControls(): ReactElement | null {
  const m = useMessages();

  const dispatch = useDispatch();

  const zoom = useAppSelector((state) => state.map.zoom);

  const embedFeatures = useAppSelector((state) => state.main.embedFeatures);

  const locate = useAppSelector((state) => state.main.locate);

  const gpsTracked = useAppSelector((state) => state.map.gpsTracked);

  const onMapRefocus = useCallback(
    (changes: Partial<MapViewState>) => {
      dispatch(mapRefocus(changes));
    },
    [dispatch],
  );

  const [map, setMap] = useState<Map>();

  useEffect(() => {
    mapPromise.then(setMap);
  }, []);

  const handleFullscreenClick = useCallback(() => {
    if (!document.exitFullscreen) {
      // unsupported
    } else if (document.fullscreenElement) {
      document.exitFullscreen();
    } else {
      document.body.requestFullscreen();
    }
  }, []);

  const [forceUpdate, setForceUpdate] = useState(0);

  useEffect(() => {
    function handler() {
      setForceUpdate(forceUpdate + 1);
    }

    document.addEventListener('fullscreenchange', handler);

    return () => {
      document.removeEventListener('fullscreenchange', handler);
    };
  }, [forceUpdate, setForceUpdate]);

  if (!map) {
    return null;
  }

  return (
    <Card className="fm-toolbar mx-2 mb-2">
      {(!window.fmEmbedded || !embedFeatures.includes('noMapSwitch')) && (
        <MapSwitchButton />
      )}


function handler() {
      setForceUpdate(forceUpdate + 1);
    }


(state) => state.map.zoom


(state) => state.main.embedFeatures


(state) => state.main.locate


(state) => state.map.gpsTracked


(changes: Partial<MapViewState>) => {
      dispatch(mapRefocus(changes));
    }


() => {
    mapPromise.then(setMap);
  }


() => {
    if (!document.exitFullscreen) {
      // unsupported
    } else if (document.fullscreenElement) {
      document.exitFullscreen();
    } else {
      document.body.requestFullscreen();
    }
  }


() => {
    function handler() {
      setForceUpdate(forceUpdate + 1);
    }

    document.addEventListener('fullscreenchange', handler);

    return () => {
      document.removeEventListener('fullscreenchange', handler);
    };
  }


() => {
      document.removeEventListener('fullscreenchange', handler);
    }


export function createCellId(): CellId {
  return uuid();
}


/**
 * Turn nbformat multiline strings (arrays of strings for simplifying diffs)
 * into strings
 */
export function demultiline(s: string | string[]): string {
  if (Array.isArray(s)) {
    return s.join("");
  }
  return s;
}


/**
 * Split string into a list of strings delimited by newlines; useful for on-disk
 * git comparisons; and is the expectation for jupyter notebooks on disk
 */
export function remultiline(s: string | string[]): string[] {
  if (Array.isArray(s)) {
    // Assume already multiline string
    return s;
  }
  // Split on newline and retain newline char
  return s.split(/(.*?(?:\r\n|\n))/g).filter(x => x !== "");
}


function isJSONKey(key: string): boolean {
  return /^application\/(.*\+)json$/.test(key);
}


// tslint:disable-next-line:max-line-length
// Taken from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze
export function deepFreeze<T>(object: T): DeepReadonly<T> {
  // Retrieve the property names defined on object
  const propNames = Object.getOwnPropertyNames(object);

  // Freeze properties before freezing self
  for (const name of propNames) {
    // getOwnPropertyNames assures us we can index on name
    const value = (object as any)[name];

    (object as any)[name] =
      value && typeof value === "object" ? deepFreeze(value) : value;
  }

  return (Object.freeze(object) as unknown) as DeepReadonly<T>;
}


export function createFrozenMediaBundle(
  mediaBundle: OnDiskMediaBundle
): Readonly<MediaBundle> {
  // Map over all the mimetypes; turning them into our in-memory format
  //
  // {
  //   "application/json": {"a": 3; "b": 2};
  //   "text/html": ["
\n"; "Hey\n"; "
"];
  //   "text/plain": "Hey"
  // }
  //
  // to
  //
  // {
  //   "application/json": {"a": 3; "b": 2};
  //   "text/html": "
\nHey\n
";
  //   "text/plain": "Hey"
  // }

  // Since we have to convert from one type to another that has conflicting
  // types; we need to hand convert it in a way that flow is able to verify
  // correctly.
  // FIXME: Is above comment still valid with the move from flow to ts?
  //
  // The way we do that is create a new object that we declare with the type
  // we want; set the keys and values we need; then seal the object with
  // Object.freeze
  const bundle: MediaBundle = {};

  for (const key in mediaBundle) {
    if (IS_VEGA.test(key)) {
      bundle[key] = JSON.stringify(mediaBundle[key]);
    } else if (typeof mediaBundle[key] === "string") {
      // Strings are immutable and can be just taken as-is.
      //
      // N.B.: This is even true of strings sent directly as the JSON root
      // object -- which Bokeh's output_notebook() is known to do -- so we
      // have to check for them even in the JSON case, as deepFreeze will fail
      // on strings.
      bundle[key] = mediaBundle[key] as string;
    } else if (!isJSONKey(key) && Array.isArray(mediaBundle[key])) {
      bundle[key] = demultiline(mediaBundle[key] as MultiLineString);
    } else {
      // we now know it's an Object of some kind (or a JSON array)
      bundle[key] = deepFreeze(mediaBundle[key]!);
    }
  }

  return Object.freeze(bundle);
}


export function createOnDiskMediaBundle(
  mediaBundle: Readonly<MediaBundle>
): OnDiskMediaBundle {
  // Return the bundle as is
  return mediaBundle;

  // The remultiline function in the code below is costly when the data payload is large
  // TODO: figure out a configurable way to reformat the string when needed.

  // However for the sake of on-disk readability we write out remultilined
  // versions of the array and string ones
  // const freshBundle: OnDiskMediaBundle = {};
  // for (const key in mediaBundle) {
  //   if (IS_VEGA.test(key)) {
  //     freshBundle[key] = JSON.parse(mediaBundle[key] as string);
  //   } else if (
  //     !isJSONKey(key) &&
  //     (typeof mediaBundle[key] === "string" || Array.isArray(mediaBundle[key]))
  //   ) {
  //     freshBundle[key] = remultiline(mediaBundle[key] as MultiLineString);
  //   } else {
  //     freshBundle[key] = mediaBundle[key];
  //   }
  // }
  // return freshBundle;
}


x => x !== ""


export interface JSONObject {
  [key: string]: JSONType;
}


export interface JSONArray extends Array<JSONType> {}


// tslint:disable:max-line-length
/**
 * Media Bundles as they exist on disk from the notebook format
 * See https://nbformat.readthedocs.io/en/latest/format_description.html#display-data for docs
 * and https://github.com/jupyter/nbformat/blob/master/nbformat/v4/nbformat.v4.schema.json for the schema
 */
// tslint:enable:max-line-length
export interface OnDiskMediaBundle {
  "text/plain"?: MultiLineString;
  "text/html"?: MultiLineString;
  "text/latex"?: MultiLineString;
  "text/markdown"?: MultiLineString;
  "application/javascript"?: MultiLineString;
  "image/png"?: MultiLineString;
  "image/jpeg"?: MultiLineString;
  "image/gif"?: MultiLineString;
  "image/svg+xml"?: MultiLineString;

  // The JSON mimetype has some corner cases because of the protocol / format
  // assuming the values in a media bundle are either:
  //
  //   * A string; which would be deserialized
  //   * An array; which would have to be assumed to be a multiline string
  //
  "application/json"?: string | string[] | {};
  "application/vdom.v1+json"?: {};
  "application/vnd.dataresource+json"?: {};
  "text/vnd.plotly.v1+html"?: MultiLineString | {};
  "application/vnd.plotly.v1+json"?: {};
  "application/geo+json"?: {};
  "application/x-nteract-model-debug+json"?: {};
  "application/vnd.vega.v2+json"?: {};
  "application/vnd.vega.v3+json"?: {};
  "application/vnd.vega.v4+json"?: {};
  "application/vnd.vega.v5+json"?: {};
  "application/vnd.vegalite.v1+json"?: {};
  "application/vnd.vegalite.v2+json"?: {};
  "application/vnd.vegalite.v3+json"?: {};
  "application/vnd.vegalite.v4+json"?: {};

  [key: string]: string | string[] | {} | undefined;
}


// Enumerating over all the media types we currently accept
export interface MediaBundle {
  "text/plain"?: string;
  "text/html"?: string;
  "text/latex"?: string;
  "text/markdown"?: string;
  "application/javascript"?: string;
  "image/png"?: string;
  "image/jpeg"?: string;
  "image/gif"?: string;
  "image/svg+xml"?: string;
  // All our JSON types can only be JSON Objects
  "application/json"?: { [key: string]: any };
  "application/vdom.v1+json"?: { [key: string]: any };
  "application/vnd.dataresource+json"?: { [key: string]: any };
  "text/vnd.plotly.v1+html"?: string | { [key: string]: any };
  "application/vnd.plotly.v1+json"?: { [key: string]: any };
  "application/geo+json"?: { [key: string]: any };
  "application/x-nteract-model-debug+json"?: { [key: string]: any };
  "application/vnd.vega.v2+json"?: { [key: string]: any };
  "application/vnd.vega.v3+json"?: { [key: string]: any };
  "application/vnd.vega.v4+json"?: { [key: string]: any };
  "application/vnd.vega.v5+json"?: { [key: string]: any };
  "application/vnd.vegalite.v1+json"?: { [key: string]: any };
  "application/vnd.vegalite.v2+json"?: { [key: string]: any };
  "application/vnd.vegalite.v3+json"?: { [key: string]: any };
  "application/vnd.vegalite.v4+json"?: { [key: string]: any };
  // Other media types can also come in that we don't recognize
  [key: string]: string | string[] | {} | undefined;
}


export type ExecutionCount = number | null;


// Mutable JSON types
export type PrimitiveImmutable = string | number | boolean | null;


export type JSONType = PrimitiveImmutable | JSONObject | JSONArray;


export type CellId = string;


// On disk multi-line strings are used to accomodate line-by-line diffs in tools
// like git and GitHub. They get converted to strings for the in-memory format.
export type MultiLineString = string | string[];


export type ImmutableJSONType =
  | PrimitiveImmutable
  | ImmutableJSONMap
  | ImmutableJSONList;


// Can't (easily) write circularly referenced types so this'll have to do for
// now
export type ImmutableJSONMap = Immutable.Map<string, any>;


export type ImmutableJSONList = Immutable.List<any>;


// A type with all ownPropertyNames also readonly; works for all JSON types
type DeepReadonly<T> = { readonly [P in keyof T]: DeepReadonly<T[P]> };


async function beginCatchEvents(frontend: puppeteer.Page) {
  await frontend.evaluate(() => {
    window.__panelShown = (evt: Event) => {
      const customEvt = evt as CustomEvent;
      window.__caughtEvents.push({name: 'DevTools.PanelShown', value: customEvt.detail.value});
    };

    window.__panelLoaded = (evt: Event) => {
      const customEvt = evt as CustomEvent;
      window.__caughtEvents.push({name: 'DevTools.PanelLoaded', value: customEvt.detail.value});
    };

    window.__actionTaken = (evt: Event) => {
      const customEvt = evt as CustomEvent;
      window.__caughtEvents.push({name: 'DevTools.ActionTaken', value: customEvt.detail.value});
    };

    window.__keyboardShortcutFired = (evt: Event) => {
      const customEvt = evt as CustomEvent;
      window.__caughtEvents.push({name: 'DevTools.KeyboardShortcutFired', value: customEvt.detail.value});
    };

    window.__issuesPanelOpenedFrom = (evt: Event) => {
      const customEvt = evt as CustomEvent;
      window.__caughtEvents.push({name: 'DevTools.IssuesPanelOpenedFrom', value: customEvt.detail.value});
    };
    window.__keybindSetSettingChanged = (evt: Event) => {
      const customEvt = evt as CustomEvent;
      window.__caughtEvents.push({name: 'DevTools.KeybindSetSettingChanged', value: customEvt.detail.value});
    };

    window.__caughtEvents = [];
    window.__beginCatchEvents = () => {
      window.addEventListener('DevTools.PanelShown', window.__panelShown);
      window.addEventListener('DevTools.PanelLoaded', window.__panelLoaded);
      window.addEventListener('DevTools.ActionTaken', window.__actionTaken);
      window.addEventListener('DevTools.KeyboardShortcutFired', window.__keyboardShortcutFired);
      window.addEventListener('DevTools.IssuesPanelOpenedFrom', window.__issuesPanelOpenedFrom);
      window.addEventListener('DevTools.KeybindSetSettingChanged', window.__keybindSetSettingChanged);
    };

    window.__endCatchEvents = () => {
      window.removeEventListener('DevTools.PanelShown', window.__panelShown);
      window.removeEventListener('DevTools.PanelLoaded', window.__panelLoaded);
      window.removeEventListener('DevTools.ActionTaken', window.__actionTaken);
      window.removeEventListener('DevTools.KeyboardShortcutFired', window.__keyboardShortcutFired);
      window.removeEventListener('DevTools.IssuesPanelOpenedFrom', window.__issuesPanelOpenedFrom);
      window.removeEventListener('DevTools.KeybindSetSettingChanged', window.__keybindSetSettingChanged);
    };

    window.__beginCatchEvents();
  });
}


async function endCatchEvents(frontend: puppeteer.Page) {
  await frontend.evaluate(() => {
    window.__endCatchEvents();
  });
}


function retrieveCapturedEvents(frontend: puppeteer.Page) {
  return frontend.evaluate(() => window.__caughtEvents);
}


async function assertCapturedEvents(expected: UserMetric[]) {
  const {frontend} = getBrowserAndPages();
  const events = await retrieveCapturedEvents(frontend);

  assert.deepEqual(events, expected);
}


() => {
    window.__panelShown = (evt: Event) => {
      const customEvt = evt as CustomEvent;
      window.__caughtEvents.push({name: 'DevTools.PanelShown', value: customEvt.detail.value});
    };

    window.__panelLoaded = (evt: Event) => {
      const customEvt = evt as CustomEvent;
      window.__caughtEvents.push({name: 'DevTools.PanelLoaded', value: customEvt.detail.value});
    };

    window.__actionTaken = (evt: Event) => {
      const customEvt = evt as CustomEvent;
      window.__caughtEvents.push({name: 'DevTools.ActionTaken', value: customEvt.detail.value});
    };

    window.__keyboardShortcutFired = (evt: Event) => {
      const customEvt = evt as CustomEvent;
      window.__caughtEvents.push({name: 'DevTools.KeyboardShortcutFired', value: customEvt.detail.value});
    };

    window.__issuesPanelOpenedFrom = (evt: Event) => {
      const customEvt = evt as CustomEvent;
      window.__caughtEvents.push({name: 'DevTools.IssuesPanelOpenedFrom', value: customEvt.detail.value});
    };
    window.__keybindSetSettingChanged = (evt: Event) => {
      const customEvt = evt as CustomEvent;
      window.__caughtEvents.push({name: 'DevTools.KeybindSetSettingChanged', value: customEvt.detail.value});
    };

    window.__caughtEvents = [];
    window.__beginCatchEvents = () => {
      window.addEventListener('DevTools.PanelShown', window.__panelShown);
      window.addEventListener('DevTools.PanelLoaded', window.__panelLoaded);
      window.addEventListener('DevTools.ActionTaken', window.__actionTaken);
      window.addEventListener('DevTools.KeyboardShortcutFired', window.__keyboardShortcutFired);
      window.addEventListener('DevTools.IssuesPanelOpenedFrom', window.__issuesPanelOpenedFrom);
      window.addEventListener('DevTools.KeybindSetSettingChanged', window.__keybindSetSettingChanged);
    };

    window.__endCatchEvents = () => {
      window.removeEventListener('DevTools.PanelShown', window.__panelShown);
      window.removeEventListener('DevTools.PanelLoaded', window.__panelLoaded);
      window.removeEventListener('DevTools.ActionTaken', window.__actionTaken);
      window.removeEventListener('DevTools.KeyboardShortcutFired', window.__keyboardShortcutFired);
      window.removeEventListener('DevTools.IssuesPanelOpenedFrom', window.__issuesPanelOpenedFrom);
      window.removeEventListener('DevTools.KeybindSetSettingChanged', window.__keybindSetSettingChanged);
    };

    window.__beginCatchEvents();
  }


(evt: Event) => {
      const customEvt = evt as CustomEvent;
      window.__caughtEvents.push({name: 'DevTools.PanelShown', value: customEvt.detail.value});
    }


(evt: Event) => {
      const customEvt = evt as CustomEvent;
      window.__caughtEvents.push({name: 'DevTools.PanelLoaded', value: customEvt.detail.value});
    }


(evt: Event) => {
      const customEvt = evt as CustomEvent;
      window.__caughtEvents.push({name: 'DevTools.ActionTaken', value: customEvt.detail.value});
    }


(evt: Event) => {
      const customEvt = evt as CustomEvent;
      window.__caughtEvents.push({name: 'DevTools.KeyboardShortcutFired', value: customEvt.detail.value});
    }


(evt: Event) => {
      const customEvt = evt as CustomEvent;
      window.__caughtEvents.push({name: 'DevTools.IssuesPanelOpenedFrom', value: customEvt.detail.value});
    }


(evt: Event) => {
      const customEvt = evt as CustomEvent;
      window.__caughtEvents.push({name: 'DevTools.KeybindSetSettingChanged', value: customEvt.detail.value});
    }


() => {
      window.addEventListener('DevTools.PanelShown', window.__panelShown);
      window.addEventListener('DevTools.PanelLoaded', window.__panelLoaded);
      window.addEventListener('DevTools.ActionTaken', window.__actionTaken);
      window.addEventListener('DevTools.KeyboardShortcutFired', window.__keyboardShortcutFired);
      window.addEventListener('DevTools.IssuesPanelOpenedFrom', window.__issuesPanelOpenedFrom);
      window.addEventListener('DevTools.KeybindSetSettingChanged', window.__keybindSetSettingChanged);
    }


() => {
      window.removeEventListener('DevTools.PanelShown', window.__panelShown);
      window.removeEventListener('DevTools.PanelLoaded', window.__panelLoaded);
      window.removeEventListener('DevTools.ActionTaken', window.__actionTaken);
      window.removeEventListener('DevTools.KeyboardShortcutFired', window.__keyboardShortcutFired);
      window.removeEventListener('DevTools.IssuesPanelOpenedFrom', window.__issuesPanelOpenedFrom);
      window.removeEventListener('DevTools.KeybindSetSettingChanged', window.__keybindSetSettingChanged);
    }


() => {
    window.__endCatchEvents();
  }


() => window.__caughtEvents


() => {
  beforeEach(async () => {
    const {frontend} = getBrowserAndPages();
    await beginCatchEvents(frontend);
  });

  afterEach(async () => {
    const {frontend} = getBrowserAndPages();
    await endCatchEvents(frontend);
  });

  it('dispatches dock and undock events', async () => {
    const {frontend} = getBrowserAndPages();
    await frontend.evaluate(() => {
      self.Host.userMetrics.actionTaken(self.Host.UserMetrics.Action.WindowDocked);
      self.Host.userMetrics.actionTaken(self.Host.UserMetrics.Action.WindowUndocked);
    });
    await assertCapturedEvents([
      {
        name: 'DevTools.ActionTaken',
        value: 1,  // WindowDocked.
      },
      {
        name: 'DevTools.ActionTaken',
        value: 2,  // WindowUndocked.
      },
    ]);
  });

  it('dispatches a metric event the console drawer', async () => {
    const {frontend} = getBrowserAndPages();

    await frontend.keyboard.press('Escape');
    await frontend.waitForSelector('.console-view');

    await assertCapturedEvents([
      {
        name: 'DevTools.PanelShown',
        value: 10,  // drawer-console-view.
      },
      {
        name: 'DevTools.KeyboardShortcutFired',
        value: 17,  // main.toggle-drawer
      },
    ]);
  });

  it('dispatches events for views', async () => {
    const {frontend} = getBrowserAndPages();

    // Head to the Timeline tab.
    await click('#tab-timeline');
    await frontend.waitForSelector('.timeline');

    await assertCapturedEvents([{
      name: 'DevTools.PanelShown',
      value: 5,  // Timeline.
    }]);
  });

  it('dispatches events for triple dot items', async () => {
    await openPanelViaMoreTools('Animations');

    await assertCapturedEvents([
      {
        name: 'DevTools.PanelShown',
        value: 10,  // 'drawer-console-view'.
      },
      {
        name: 'DevTools.PanelShown',
        value: 11,  // 'drawer-animations'.
      },
    ]);
  });

  it('dispatches events for opening issues drawer via hamburger menu', async () => {
    await openPanelViaMoreTools('Issues');

    await assertCapturedEvents([
      {
        name: 'DevTools.IssuesPanelOpenedFrom',
        value: 3,  // 'HamburgerMenu'.
      },
      {
        name: 'DevTools.PanelShown',
        value: 10,  // 'drawer-console-view'.
      },
      {
        name: 'DevTools.PanelShown',
        value: 37,  // 'drawer-issues-pane'.
      },
    ]);
  });

  it('dispatches an event when F1 is used to open settings', async () => {
    const {frontend} = getBrowserAndPages();

    await frontend.keyboard.press('F1');
    await waitFor('.settings-window-main');

    await assertCapturedEvents([
      {
        name: 'DevTools.PanelShown',
        value: 29,
      },
      {
        name: 'DevTools.KeyboardShortcutFired',
        value: 22,  // settings.show
      },
    ]);
  });

  it('dispatches an event when Ctrl/Meta+F8 is used to deactivate breakpoints', async () => {
    const {frontend} = getBrowserAndPages();

    await click('#tab-sources');
    await waitFor('#sources-panel-sources-view');

    switch (platform) {
      case 'mac':
        await frontend.keyboard.down('Meta');
        break;

      case 'linux':
      case 'win32':
        await frontend.keyboard.down('Control');
        break;
    }

    await frontend.keyboard.press('F8');

    switch (platform) {
      case 'mac':
        await frontend.keyboard.up('Meta');
        break;

      case 'linux':
      case 'win32':
        await frontend.keyboard.up('Control');
        break;
    }

    await waitFor('[aria-label="Activate breakpoints"]');

    await assertCapturedEvents([
      {
        name: 'DevTools.PanelShown',
        value: 4,  // sources
      },
      {
        name: 'DevTools.KeyboardShortcutFired',
        value: 0,  // OtherShortcut
      },
    ]);
  });

  it('dispatches an event when the keybindSet setting is changed', async () => {
    const {frontend} = getBrowserAndPages();
    await enableExperiment('customKeyboardShortcuts');
    // enableExperiment reloads the DevTools and removes our listeners
    await beginCatchEvents(frontend);

    await frontend.keyboard.press('F1');
    await waitFor('.settings-window-main');
    await click('[aria-label="Shortcuts"]');
    await waitFor('.keybinds-set-select');

    const keybindSetSelect = await $('.keybinds-set-select select') as puppeteer.ElementHandle<HTMLSelectElement>;
    keybindSetSelect.select('vsCode');

    await assertCapturedEvents([
      {
        name: 'DevTools.PanelShown',
        value: 29,  // settings-preferences
      },
      {
        name: 'DevTools.KeyboardShortcutFired',
        value: 22,  // settings.show
      },
      {
        name: 'DevTools.PanelShown',
        value: 38,  // settings-keybinds
      },
      {
        name: 'DevTools.KeybindSetSettingChanged',
        value: 1,  // vsCode
      },
    ]);
  });

  // Flaky test
  it.skip('[crbug.com/1071850]: tracks panel loading', async () => {
    // We specify the selected panel here because the default behavior is to go to the
    // elements panel, but this means we won't get the PanelLoaded event. Instead we
    // request that the resetPages helper sets the timeline as the target panel, and
    // we wait for the timeline in the test. This means, in turn, we get the PanelLoaded
    // event.
    await reloadDevTools({selectedPanel: {name: 'timeline'}});
    const {frontend} = getBrowserAndPages();

    await beginCatchEvents(frontend);

    await waitFor('.timeline');

    await assertCapturedEvents([
      {
        name: 'DevTools.PanelLoaded',
        value: {
          histogramName: 'DevTools.Launch.Timeline',
          panelName: 'timeline',
        },
      },
    ]);
  });
}


async () => {
    const {frontend} = getBrowserAndPages();
    await beginCatchEvents(frontend);
  }


async () => {
    const {frontend} = getBrowserAndPages();
    await endCatchEvents(frontend);
  }


async () => {
    const {frontend} = getBrowserAndPages();
    await frontend.evaluate(() => {
      self.Host.userMetrics.actionTaken(self.Host.UserMetrics.Action.WindowDocked);
      self.Host.userMetrics.actionTaken(self.Host.UserMetrics.Action.WindowUndocked);
    });
    await assertCapturedEvents([
      {
        name: 'DevTools.ActionTaken',
        value: 1,  // WindowDocked.
      },
      {
        name: 'DevTools.ActionTaken',
        value: 2,  // WindowUndocked.
      },
    ]);
  }


() => {
      self.Host.userMetrics.actionTaken(self.Host.UserMetrics.Action.WindowDocked);
      self.Host.userMetrics.actionTaken(self.Host.UserMetrics.Action.WindowUndocked);
    }


async () => {
    const {frontend} = getBrowserAndPages();

    await frontend.keyboard.press('Escape');
    await frontend.waitForSelector('.console-view');

    await assertCapturedEvents([
      {
        name: 'DevTools.PanelShown',
        value: 10,  // drawer-console-view.
      },
      {
        name: 'DevTools.KeyboardShortcutFired',
        value: 17,  // main.toggle-drawer
      },
    ]);
  }


async () => {
    const {frontend} = getBrowserAndPages();

    // Head to the Timeline tab.
    await click('#tab-timeline');
    await frontend.waitForSelector('.timeline');

    await assertCapturedEvents([{
      name: 'DevTools.PanelShown',
      value: 5,  // Timeline.
    }]);
  }


async () => {
    await openPanelViaMoreTools('Animations');

    await assertCapturedEvents([
      {
        name: 'DevTools.PanelShown',
        value: 10,  // 'drawer-console-view'.
      },
      {
        name: 'DevTools.PanelShown',
        value: 11,  // 'drawer-animations'.
      },
    ]);
  }


async () => {
    await openPanelViaMoreTools('Issues');

    await assertCapturedEvents([
      {
        name: 'DevTools.IssuesPanelOpenedFrom',
        value: 3,  // 'HamburgerMenu'.
      },
      {
        name: 'DevTools.PanelShown',
        value: 10,  // 'drawer-console-view'.
      },
      {
        name: 'DevTools.PanelShown',
        value: 37,  // 'drawer-issues-pane'.
      },
    ]);
  }


async () => {
    const {frontend} = getBrowserAndPages();

    await frontend.keyboard.press('F1');
    await waitFor('.settings-window-main');

    await assertCapturedEvents([
      {
        name: 'DevTools.PanelShown',
        value: 29,
      },
      {
        name: 'DevTools.KeyboardShortcutFired',
        value: 22,  // settings.show
      },
    ]);
  }


async () => {
    const {frontend} = getBrowserAndPages();

    await click('#tab-sources');
    await waitFor('#sources-panel-sources-view');

    switch (platform) {
      case 'mac':
        await frontend.keyboard.down('Meta');
        break;

      case 'linux':
      case 'win32':
        await frontend.keyboard.down('Control');
        break;
    }

    await frontend.keyboard.press('F8');

    switch (platform) {
      case 'mac':
        await frontend.keyboard.up('Meta');
        break;

      case 'linux':
      case 'win32':
        await frontend.keyboard.up('Control');
        break;
    }

    await waitFor('[aria-label="Activate breakpoints"]');

    await assertCapturedEvents([
      {
        name: 'DevTools.PanelShown',
        value: 4,  // sources
      },
      {
        name: 'DevTools.KeyboardShortcutFired',
        value: 0,  // OtherShortcut
      },
    ]);
  }


async () => {
    const {frontend} = getBrowserAndPages();
    await enableExperiment('customKeyboardShortcuts');
    // enableExperiment reloads the DevTools and removes our listeners
    await beginCatchEvents(frontend);

    await frontend.keyboard.press('F1');
    await waitFor('.settings-window-main');
    await click('[aria-label="Shortcuts"]');
    await waitFor('.keybinds-set-select');

    const keybindSetSelect = await $('.keybinds-set-select select') as puppeteer.ElementHandle<HTMLSelectElement>;
    keybindSetSelect.select('vsCode');

    await assertCapturedEvents([
      {
        name: 'DevTools.PanelShown',
        value: 29,  // settings-preferences
      },
      {
        name: 'DevTools.KeyboardShortcutFired',
        value: 22,  // settings.show
      },
      {
        name: 'DevTools.PanelShown',
        value: 38,  // settings-keybinds
      },
      {
        name: 'DevTools.KeybindSetSettingChanged',
        value: 1,  // vsCode
      },
    ]);
  }


async () => {
    // We specify the selected panel here because the default behavior is to go to the
    // elements panel, but this means we won't get the PanelLoaded event. Instead we
    // request that the resetPages helper sets the timeline as the target panel, and
    // we wait for the timeline in the test. This means, in turn, we get the PanelLoaded
    // event.
    await reloadDevTools({selectedPanel: {name: 'timeline'}});
    const {frontend} = getBrowserAndPages();

    await beginCatchEvents(frontend);

    await waitFor('.timeline');

    await assertCapturedEvents([
      {
        name: 'DevTools.PanelLoaded',
        value: {
          histogramName: 'DevTools.Launch.Timeline',
          panelName: 'timeline',
        },
      },
    ]);
  }


interface UserMetric {
  name: string;
  value: string|number|LoadMetric;
}


interface LoadMetric {
  histogramName: string;
  panelName: string;
}


interface UserMetrics {
  Action: {[name: string]: number};
}


interface Window {
    __caughtEvents: UserMetric[];
    __beginCatchEvents: () => void;
    __endCatchEvents: () => void;
    __panelLoaded: (evt: Event) => void;
    __panelShown: (evt: Event) => void;
    __actionTaken: (evt: Event) => void;
    __keyboardShortcutFired: (evt: Event) => void;
    __issuesPanelOpenedFrom: (evt: Event) => void;
    __keybindSetSettingChanged: (evt: Event) => void;
    Host: {UserMetrics: UserMetrics; userMetrics: {actionTaken(name: number): void;}};
    UI: {inspectorView: {_showDrawer(show: boolean): void; showView(name: string): void;}};
  }


async (t) => {
		await testLint(
			t,
			dedent`
        import foo from './testdummy.ts';
        import {bar} from './testdummy.ts';
        import type {fooType} from './testdummy.ts';

        const typedFoo: fooType = {
          type: 'foo'
        };
      `,
			{category: "lint/js/duplicateImportSource"},
		);
	}


export class Angular4Page {
  navigateTo() {
    return browser.get('/');
  }

  getParagraphText() {
    return element(by.css('app-root h1')).getText();
  }
}


() => {
  let testHook: TestHook;

  beforeEach(() => {
    testHook = new TestHook();
  });

  it('should execute', async () => {
    await testHook.execute({ name: 'John Smith' });
  });
}


() => {
    testHook = new TestHook();
  }


async () => {
    await testHook.execute({ name: 'John Smith' });
  }


class TestHook extends Hook<Person> {
  async execute(data: Person): Promise<void> {
    await this.sendToEther(data);
  }

  private async sendToEther(data: Person) {
    const promise = Promise.resolve(data);
    const response = await promise;
  }
}


interface Person {
  name: string;
}


async execute(data: Person): Promise<void> {
    await this.sendToEther(data);
  }


private async sendToEther(data: Person) {
    const promise = Promise.resolve(data);
    const response = await promise;
  }


@Injectable()
@Component({
  selector: 'app-session-list',
  templateUrl: './session-list.component.html',
  styleUrls: ['./session-list.component.css']
})
export class SessionListComponent implements OnInit {

  @Input() sessions: ISession[];

  constructor() { }

  ngOnInit() {
  }

}


export async function getSource(
    githubManager: IGithubManager,
    settings: ISettings,
    repositoryUrl: string,
    repositoryPath: string,
    ref: string, // ref
    fetchDepth = 0,
): Promise<void> {
    // Repository URL
    core.info(`Cloning repository: ${repositoryUrl}`);

    // Remove conflicting file path
    if (fs.existsSync(repositoryPath)) {
        await io.rmRF(repositoryPath);
    }

    // Create directory
    let isExisting = true;

    if (!fs.existsSync(repositoryPath)) {
        isExisting = false;

        await io.mkdirP(repositoryPath);
    }

    // Git command manager
    core.startGroup("Getting Git version info");
    const git = await gitCommandManager(repositoryPath);
    core.endGroup();

    // Prepare existing directory, otherwise recreate
    if (isExisting) {
        core.info(`Deleting the contents of '${repositoryPath}'`);

        for (const file of await fs.promises.readdir(repositoryPath)) {
            await io.rmRF(path.join(repositoryPath, file));
        }
    }

    if (!git) {
        // Downloading using REST API
        core.info(`The repository will be downloaded using the GitHub REST API`);
        core.info(`To create a local Git repository instead, add Git ${MinimumGitVersion} or higher to the PATH`);

        if (settings.sshKey) {
            throw new Error(
                `Input 'ssh-key' not supported when falling back to download using the GitHub REST API. To create a local Git repository instead, add Git ${MinimumGitVersion} or higher to the PATH.`,
            );
        }

        const [templateRepositoryOwner, templateRepositoryName] = repositoryPath.split("/");

        await githubManager.repos.downloadRepository(templateRepositoryOwner, templateRepositoryName, ref);
        return;
    }

    // Save state for POST action
    stateHelper.setTemplateRepositoryPath(repositoryPath);

    // Initialize the repository
    if (!fs.existsSync(path.join(repositoryPath, ".git"))) {
        core.startGroup("Initializing the repository");
        await git.init();

        await git.remoteAdd("origin", repositoryUrl);
        core.endGroup();
    }

    // Disable automatic garbage collection
    core.startGroup("Disabling automatic garbage collection");
    if (!(await git.tryDisableAutomaticGarbageCollection())) {
        core.warning(`Unable to turn off git automatic garbage collection. The git fetch operation may trigger garbage collection and cause a delay.`);
    }
    core.endGroup();

    const authHelper = new GitAuthHelper(git, settings);

    try {
        // Configure auth
        core.startGroup("Setting up auth");
        await authHelper.configureAuth();
        core.endGroup();

        // Fetch
        core.startGroup("Fetching the repository");
        const refSpec = refHelper.getRefSpec(ref);
        await git.fetch(fetchDepth, refSpec);
        core.endGroup();

        // Checkout info
        core.startGroup("Determining the checkout info");
        const checkoutInfo = await refHelper.getCheckoutInfo(git, ref);
        core.endGroup();

        // Checkout
        core.startGroup("Checking out the ref");
        await git.checkout(checkoutInfo.ref, checkoutInfo.startPoint);
        core.endGroup();

        // Dump some info about the checked out commit
        await git.log1();
    } finally {
        // Remove auth
        if (!settings.persistCredentials) {
            core.startGroup("Removing auth");
            await authHelper.removeAuth();
            core.endGroup();
        }
    }
}


async function gitCommandManager(repositoryPath: string): Promise<IGitCommandManager | undefined> {
    core.info(`Working directory is '${repositoryPath}'`);

    try {
        const manager = await createCommandManager(repositoryPath);

        manager.checkGitVersion();

        return manager;
    } catch (err) {
        // Otherwise fallback to REST API
        return undefined;
    }
}


(e) =>
        this.payments.push({
          id: '',
          cardName: newCard.cardName,
          cardNumber: newCard.cardNumber,
          securityCode: newCard.securityCode,
          cardExpirationDate: newCard.cardExpirationDate,
        })


(e) => console.error(e)


@Component({
  selector: 'uic-payment',
  templateUrl: './payment.component.html',
})
/**
 * Class representing a user's payment information
 */
export class PaymentComponent {
  @Input() payments: Payment[];
  @Input() accountid: string;
  @Output() paymentsEdited = new EventEmitter();

  /**
   * Represents the _Payment Component_ 'constructor' method
   */
  constructor(private readonly accountService: AccountService) {}

  /**
   * Adds a new set of payment information
   * @param newCard Payment
   */
  addCard(newCard: PostPayment) {
    newCard.accountId = this.accountid;
    this.accountService.postPayment(newCard).subscribe(
      (e) =>
        this.payments.push({
          id: '',
          cardName: newCard.cardName,
          cardNumber: newCard.cardNumber,
          securityCode: newCard.securityCode,
          cardExpirationDate: newCard.cardExpirationDate,
        }),
      (e) => console.error(e)
    );
    console.log(newCard);
  }
}


/**
   * Adds a new set of payment information
   * @param newCard Payment
   */
addCard(newCard: PostPayment) {
    newCard.accountId = this.accountid;
    this.accountService.postPayment(newCard).subscribe(
      (e) =>
        this.payments.push({
          id: '',
          cardName: newCard.cardName,
          cardNumber: newCard.cardNumber,
          securityCode: newCard.securityCode,
          cardExpirationDate: newCard.cardExpirationDate,
        }),
      (e) => console.error(e)
    );
    console.log(newCard);
  }


export function base64(s: string): string {
	const buffer = Buffer.from(s);
	return buffer.toString('base64');
}


export function compareSubstring(
	a: string,
	b: string,
	aStart: number = 0,
	aEnd: number = a.length,
	bStart: number = 0,
	bEnd: number = b.length,
): number {
	for (; aStart < aEnd && bStart < bEnd; aStart++, bStart++) {
		const codeA = a.charCodeAt(aStart);
		const codeB = b.charCodeAt(bStart);
		if (codeA < codeB) {
			return -1;
		} else if (codeA > codeB) {
			return 1;
		}
	}
	const aLen = aEnd - aStart;
	const bLen = bEnd - bStart;
	if (aLen < bLen) {
		return -1;
	} else if (aLen > bLen) {
		return 1;
	}
	return 0;
}


export function compareSubstringIgnoreCase(
	a: string,
	b: string,
	aStart: number = 0,
	aEnd: number = a.length,
	bStart: number = 0,
	bEnd: number = b.length,
): number {
	for (; aStart < aEnd && bStart < bEnd; aStart++, bStart++) {
		const codeA = a.charCodeAt(aStart);
		const codeB = b.charCodeAt(bStart);

		if (codeA === codeB) {
			// equal
			continue;
		}

		const diff = codeA - codeB;
		if (diff === 32 && isUpperAsciiLetter(codeB)) {
			//codeB =[65-90] && codeA =[97-122]
			continue;
		} else if (diff === -32 && isUpperAsciiLetter(codeA)) {
			//codeB =[97-122] && codeA =[65-90]
			continue;
		}

		if (isLowerAsciiLetter(codeA) && isLowerAsciiLetter(codeB)) {
			//
			return diff;
		}
		return compareSubstring(a.toLowerCase(), b.toLowerCase(), aStart, aEnd, bStart, bEnd);
	}

	const aLen = aEnd - aStart;
	const bLen = bEnd - bStart;

	if (aLen < bLen) {
		return -1;
	} else if (aLen > bLen) {
		return 1;
	}

	return 0;
}


export function escapeMarkdown(s: string, options: { quoted?: boolean } = {}): string {
	s = s
		// Escape markdown
		.replace(escapeMarkdownRegex, '\\$&')
		// Escape markdown header (since the above regex won't match it)
		.replace(escapeMarkdownHeaderRegex, '\u200b===');

	if (!options.quoted) return s;

	// Keep under the same block-quote but with line breaks
	return s.replace(markdownQuotedRegex, '\t\n>  ');
}


export function equalsIgnoreCase(a: string | null | undefined, b: string | null | undefined): boolean {
	if (a == null && b == null) return true;
	if (a == null || b == null) return false;
	return a.localeCompare(b, undefined, { sensitivity: 'accent' }) === 0;
}


export function escapeRegex(s: string) {
	return s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
}


export function getCommonBase(s1: string, s2: string, delimiter: string) {
	let char;
	let index = 0;
	for (let i = 0; i < s1.length; i++) {
		char = s1[i];
		if (char !== s2[i]) break;

		if (char === delimiter) {
			index = i;
		}
	}

	return index > 0 ? s1.substring(0, index + 1) : undefined;
}


export function getDurationMilliseconds(start: [number, number]) {
	const [secs, nanosecs] = process.hrtime(start);
	return secs * 1000 + Math.floor(nanosecs / 1000000);
}


export function getSuperscript(num: number) {
	return superscripts[num - 1] ?? '';
}


export function getTokensFromTemplate(template: string) {
	const tokens: { key: string; options: TokenOptions }[] = [];

	let match;
	do {
		match = tokenRegex.exec(template);
		if (match == null) break;

		let [, prefix, key, truncateTo, option, suffix] = match;
		// Check for a prefix group
		if (prefix != null) {
			match = tokenGroupRegex.exec(prefix);
			if (match != null) {
				[, prefix] = match;
				prefix = prefix.replace(tokenGroupCharacterEscapedRegex, tokenGroupCharacter);
			}
		}

		// Check for a suffix group
		if (suffix != null) {
			match = tokenGroupRegex.exec(suffix);
			if (match != null) {
				[, suffix] = match;
				suffix = suffix.replace(tokenGroupCharacterEscapedRegex, tokenGroupCharacter);
			}
		}

		tokens.push({
			key: key,
			options: {
				collapseWhitespace: option === '?',
				padDirection: option === '-' ? 'left' : 'right',
				prefix: prefix || undefined,
				suffix: suffix || undefined,
				truncateTo: truncateTo == null ? undefined : parseInt(truncateTo, 10),
			},
		});
	} while (true);

	return tokens;
}


export function interpolate(template: string, context: object | undefined): string {
	if (template == null || template.length === 0) return template;
	if (context == null) return template.replace(tokenSanitizeRegex, emptyStr);

	let fn = interpolationMap.get(template);
	if (fn == null) {
		// eslint-disable-next-line @typescript-eslint/no-implied-eval
		fn = new Function(`return \`${template.replace(tokenSanitizeRegex, tokenSanitizeReplacement)}\`;`);
		interpolationMap.set(template, fn);
	}

	return fn.call(context);
}


export async function interpolateAsync(template: string, context: object | undefined): Promise<string> {
	if (template == null || template.length === 0) return template;
	if (context == null) return template.replace(tokenSanitizeRegex, emptyStr);

	let fn = interpolationAsyncMap.get(template);
	if (fn == null) {
		// // eslint-disable-next-line @typescript-eslint/no-implied-eval
		const body = `return \`${template.replace(tokenSanitizeRegex, tokenSanitizeReplacementAsync)}\`;`;
		fn = new AsyncFunction(body);
		interpolationAsyncMap.set(template, fn);
	}

	const value = await fn.call(context);
	return value;
}


export function isLowerAsciiLetter(code: number): boolean {
	return code >= CharCode.a && code <= CharCode.z;
}


export function isUpperAsciiLetter(code: number): boolean {
	return code >= CharCode.A && code <= CharCode.Z;
}


export function* lines(s: string, char: string = '\n'): IterableIterator<string> {
	let i = 0;
	while (i < s.length) {
		let j = s.indexOf(char, i);
		if (j === -1) {
			j = s.length;
		}

		yield s.substring(i, j);
		i = j + 1;
	}
}


export function md5(s: string, encoding: BinaryToTextEncoding = 'base64'): string {
	return createHash('md5').update(s).digest(encoding);
}


export function normalizePath(
	fileName: string,
	options: { addLeadingSlash?: boolean; stripTrailingSlash?: boolean } = { stripTrailingSlash: true },
) {
	if (fileName == null || fileName.length === 0) return fileName;

	let normalized = fileName.replace(pathNormalizeRegex, '/');

	const { addLeadingSlash, stripTrailingSlash } = { stripTrailingSlash: true, ...options };

	if (stripTrailingSlash) {
		normalized = normalized.replace(pathStripTrailingSlashRegex, emptyStr);
	}

	if (addLeadingSlash && normalized.charCodeAt(0) !== CharCode.Slash) {
		normalized = `/${normalized}`;
	}

	if (isWindows) {
		// Ensure that drive casing is normalized (lower case)
		normalized = normalized.replace(driveLetterNormalizeRegex, (drive: string) => drive.toLowerCase());
	}

	return normalized;
}


export function pad(s: string, before: number = 0, after: number = 0, padding: string = '\u00a0') {
	if (before === 0 && after === 0) return s;

	return `${before === 0 ? emptyStr : padding.repeat(before)}${s}${after === 0 ? emptyStr : padding.repeat(after)}`;
}


export function padLeft(s: string, padTo: number, padding: string = '\u00a0', width?: number) {
	const diff = padTo - (width ?? getWidth(s));
	return diff <= 0 ? s : padding.repeat(diff) + s;
}


export function padLeftOrTruncate(s: string, max: number, padding?: string, width?: number) {
	width = width ?? getWidth(s);
	if (width < max) return padLeft(s, max, padding, width);
	if (width > max) return truncate(s, max, undefined, width);
	return s;
}


export function padRight(s: string, padTo: number, padding: string = '\u00a0', width?: number) {
	const diff = padTo - (width ?? getWidth(s));
	return diff <= 0 ? s : s + padding.repeat(diff);
}


export function padOrTruncate(s: string, max: number, padding?: string, width?: number) {
	const left = max < 0;
	max = Math.abs(max);

	width = width ?? getWidth(s);
	if (width < max) return left ? padLeft(s, max, padding, width) : padRight(s, max, padding, width);
	if (width > max) return truncate(s, max, undefined, width);
	return s;
}


export function padRightOrTruncate(s: string, max: number, padding?: string, width?: number) {
	width = width ?? getWidth(s);
	if (width < max) return padRight(s, max, padding, width);
	if (width > max) return truncate(s, max);
	return s;
}


export function pluralize(
	s: string,
	count: number,
	options?: {
		/** Controls the character/string between the count and the string */
		infix?: string;
		/** Formats the count */
		format?: (count: number) => string | undefined;
		/** Controls if only the string should be included */
		only?: boolean;
		/** Controls the plural version of the string */
		plural?: string;
		/** Controls the string for a zero value */
		zero?: string;
	},
) {
	if (options == null) return `${count} ${s}${count === 1 ? emptyStr : 's'}`;

	const suffix = count === 1 ? s : options.plural ?? `${s}s`;
	if (options.only) return suffix;

	return `${count === 0 ? options.zero ?? count : options.format?.(count) ?? count}${options.infix ?? ' '}${suffix}`;
}


export function sanitizeForFileSystem(s: string, replacement: string = '_') {
	if (!s) return s;
	return s.replace(illegalCharsForFSRegex, replacement);
}


export function sha1(s: string, encoding: BinaryToTextEncoding = 'base64'): string {
	return createHash('sha1').update(s).digest(encoding);
}


export function splitLast(s: string, splitter: string) {
	const index = s.lastIndexOf(splitter);
	if (index === -1) return [s];

	return [s.substr(index), s.substring(0, index - 1)];
}


export function splitSingle(s: string, splitter: string) {
	const parts = s.split(splitter, 1);
	const first = parts[0];
	return first.length === s.length ? parts : [first, s.substr(first.length + 1)];
}


export function truncate(s: string, truncateTo: number, ellipsis: string = '\u2026', width?: number) {
	if (!s) return s;
	if (truncateTo <= 1) return ellipsis;

	width = width ?? getWidth(s);
	if (width <= truncateTo) return s;
	if (width === s.length) return `${s.substring(0, truncateTo - 1)}${ellipsis}`;

	// Skip ahead to start as far as we can by assuming all the double-width characters won't be truncated
	let chars = Math.floor(truncateTo / (width / s.length));
	let count = getWidth(s.substring(0, chars));
	while (count < truncateTo) {
		count += getWidth(s[chars++]);
	}

	if (count >= truncateTo) {
		chars--;
	}

	return `${s.substring(0, chars)}${ellipsis}`;
}


export function truncateLeft(s: string, truncateTo: number, ellipsis: string = '\u2026', width?: number) {
	if (!s) return s;
	if (truncateTo <= 1) return ellipsis;

	width = width ?? getWidth(s);
	if (width <= truncateTo) return s;
	if (width === s.length) return `${ellipsis}${s.substring(width - truncateTo)}`;

	// Skip ahead to start as far as we can by assuming all the double-width characters won't be truncated
	let chars = Math.floor(truncateTo / (width / s.length));
	let count = getWidth(s.substring(0, chars));
	while (count < truncateTo) {
		count += getWidth(s[chars++]);
	}

	if (count >= truncateTo) {
		chars--;
	}

	return `${ellipsis}${s.substring(s.length - chars)}`;
}


export function truncateMiddle(s: string, truncateTo: number, ellipsis: string = '\u2026') {
	if (!s) return s;
	if (truncateTo <= 1) return ellipsis;

	const width = getWidth(s);
	if (width <= truncateTo) return s;

	return `${s.slice(0, Math.floor(truncateTo / 2) - 1)}${ellipsis}${s.slice(width - Math.ceil(truncateTo / 2))}`;
}


// See sindresorhus/string-width
export function getWidth(s: string): number {
	if (s == null || s.length === 0) return 0;

	// Shortcut to avoid needless string `RegExp`s, replacements, and allocations
	if (!containsNonAsciiRegex.test(s)) return s.length;

	s = s.replace(cachedAnsiRegex, emptyStr);

	let count = 0;
	let emoji = 0;
	let joiners = 0;

	const graphemes = [...s];
	for (let i = 0; i < graphemes.length; i++) {
		const code = graphemes[i].codePointAt(0)!;

		// Ignore control characters
		if (code <= 0x1f || (code >= 0x7f && code <= 0x9f)) continue;

		// Ignore combining characters
		if (code >= 0x300 && code <= 0x36f) continue;

		if (
			(code >= 0x1f600 && code <= 0x1f64f) || // Emoticons
			(code >= 0x1f300 && code <= 0x1f5ff) || // Misc Symbols and Pictographs
			(code >= 0x1f680 && code <= 0x1f6ff) || // Transport and Map
			(code >= 0x2600 && code <= 0x26ff) || // Misc symbols
			(code >= 0x2700 && code <= 0x27bf) || // Dingbats
			(code >= 0xfe00 && code <= 0xfe0f) || // Variation Selectors
			(code >= 0x1f900 && code <= 0x1f9ff) || // Supplemental Symbols and Pictographs
			(code >= 65024 && code <= 65039) || // Variation selector
			(code >= 8400 && code <= 8447) // Combining Diacritical Marks for Symbols
		) {
			if (code >= 0x1f3fb && code <= 0x1f3ff) continue; // emoji modifier fitzpatrick type

			emoji++;
			count += 2;
			continue;
		}

		// Ignore zero-width joiners '\u200d'
		if (code === 8205) {
			joiners++;
			count -= 2;
			continue;
		}

		// Surrogates
		if (code > 0xffff) {
			i++;
		}

		count += isFullwidthCodePoint(code) ? 2 : 1;
	}

	const offset = emoji - joiners;
	if (offset > 1) {
		count += offset - 1;
	}
	return count;
}