diff --git a/src/app/components/editors/gameobject/gameobject-template/gameobject-template.component.html b/src/app/components/editors/gameobject/gameobject-template/gameobject-template.component.html index 0def2cee3d5..3b03ef51c07 100644 --- a/src/app/components/editors/gameobject/gameobject-template/gameobject-template.component.html +++ b/src/app/components/editors/gameobject/gameobject-template/gameobject-template.component.html @@ -84,7 +84,13 @@
- Gameobject Data + + Gameobject Data + +
diff --git a/src/app/components/editors/gameobject/gameobject-template/gameobject-template.component.ts b/src/app/components/editors/gameobject/gameobject-template/gameobject-template.component.ts index d2e048e612a..e1e133b847a 100644 --- a/src/app/components/editors/gameobject/gameobject-template/gameobject-template.component.ts +++ b/src/app/components/editors/gameobject/gameobject-template/gameobject-template.component.ts @@ -18,6 +18,8 @@ export class GameobjectTemplateComponent extends SingleRowEditorComponent { + + /* istanbul ignore next */ // because of: https://github.com/gotwarlost/istanbul/issues/690 + constructor( + public editorService: DisenchantLootTemplateService, + public handlerService: ItemHandlerService, + ) { + super(editorService, handlerService); + } +} diff --git a/src/app/components/editors/item/disenchant-loot-template/disenchant-loot-template.integration.spec.ts b/src/app/components/editors/item/disenchant-loot-template/disenchant-loot-template.integration.spec.ts new file mode 100644 index 00000000000..cd60f45930e --- /dev/null +++ b/src/app/components/editors/item/disenchant-loot-template/disenchant-loot-template.integration.spec.ts @@ -0,0 +1,286 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { RouterTestingModule } from '@angular/router/testing'; +import { of } from 'rxjs'; +import Spy = jasmine.Spy; + +import { DisenchantLootTemplateComponent } from './disenchant-loot-template.component'; +import { DisenchantLootTemplateModule } from './disenchant-loot-template.module'; +import { QueryService } from '../../../../services/query.service'; +import { DisenchantLootTemplate } from '../../../../types/disenchant-loot-template.type'; +import { ItemHandlerService } from '../../../../services/handlers/item-handler.service'; +import { MultiRowEditorPageObject } from '../../../../test-utils/multi-row-editor-page-object'; +import { DisenchantLootTemplateService } from '../../../../services/editors/item/disenchant-loot-template.service'; + +class DisenchantLootTemplatePage extends MultiRowEditorPageObject {} + +describe('DisenchantLootTemplate integration tests', () => { + let component: DisenchantLootTemplateComponent; + let fixture: ComponentFixture; + let queryService: QueryService; + let querySpy: Spy; + let handlerService: ItemHandlerService; + let page: DisenchantLootTemplatePage; + + const id = 1234; + + const originalRow0 = new DisenchantLootTemplate(); + const originalRow1 = new DisenchantLootTemplate(); + const originalRow2 = new DisenchantLootTemplate(); + originalRow0.Entry = originalRow1.Entry = originalRow2.Entry = id; + originalRow0.Item = 0; + originalRow1.Item = 1; + originalRow2.Item = 2; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ + DisenchantLootTemplateModule, + RouterTestingModule, + ], + }) + .compileComponents(); + })); + + function setup(creatingNew: boolean, lootId = id) { + spyOn(TestBed.get(DisenchantLootTemplateService), 'getLootId').and.returnValue(of( + { results: [{ lootId }] } + )); + + handlerService = TestBed.get(ItemHandlerService); + handlerService['_selected'] = `${id}`; + handlerService.isNew = creatingNew; + + queryService = TestBed.get(QueryService); + querySpy = spyOn(queryService, 'query').and.returnValue(of()); + + spyOn(queryService, 'selectAll').and.returnValue(of( + { results: creatingNew ? [] : [originalRow0, originalRow1, originalRow2] } + )); + + fixture = TestBed.createComponent(DisenchantLootTemplateComponent); + component = fixture.componentInstance; + page = new DisenchantLootTemplatePage(fixture); + fixture.autoDetectChanges(true); + fixture.detectChanges(); + } + + describe('Creating new', () => { + beforeEach(() => setup(true)); + + it('should correctly initialise', () => { + page.expectDiffQueryToBeEmpty(); + page.expectFullQueryToBeEmpty(); + expect(page.formError.hidden).toBe(true); + expect(page.addNewRowBtn.disabled).toBe(false); + expect(page.deleteSelectedRowBtn.disabled).toBe(true); + expect(page.getInputById('Item').disabled).toBe(true); + expect(page.getInputById('Reference').disabled).toBe(true); + expect(page.getInputById('Chance').disabled).toBe(true); + expect(page.getInputById('QuestRequired').disabled).toBe(true); + expect(page.getInputById('LootMode').disabled).toBe(true); + expect(page.getInputById('GroupId').disabled).toBe(true); + expect(page.getInputById('MinCount').disabled).toBe(true); + expect(page.getInputById('MaxCount').disabled).toBe(true); + expect(page.getInputById('Comment').disabled).toBe(true); + expect(page.getEditorTableRowsCount()).toBe(0); + }); + + it('adding new rows and executing the query should correctly work', () => { + const expectedQuery = 'DELETE FROM `disenchant_loot_template` WHERE (`Entry` = 1234) AND (`Item` IN (0, 1, 2));\n' + + 'INSERT INTO `disenchant_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, `GroupId`, ' + + '`MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 0, 0, 100, 0, 1, 0, 1, 1, \'\'),\n' + + '(1234, 1, 0, 100, 0, 1, 0, 1, 1, \'\'),\n' + + '(1234, 2, 0, 100, 0, 1, 0, 1, 1, \'\');'; + querySpy.calls.reset(); + + page.addNewRow(); + expect(page.getEditorTableRowsCount()).toBe(1); + page.addNewRow(); + expect(page.getEditorTableRowsCount()).toBe(2); + page.addNewRow(); + expect(page.getEditorTableRowsCount()).toBe(3); + page.clickExecuteQuery(); + + page.expectDiffQueryToContain(expectedQuery); + expect(querySpy).toHaveBeenCalledTimes(1); + expect(querySpy.calls.mostRecent().args[0]).toContain(expectedQuery); + }); + + it('adding a row and changing its values should correctly update the queries', () => { + page.addNewRow(); + page.expectDiffQueryToContain( + 'DELETE FROM `disenchant_loot_template` WHERE (`Entry` = 1234) AND (`Item` IN (0));\n' + + 'INSERT INTO `disenchant_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, ' + + '`GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 0, 0, 100, 0, 1, 0, 1, 1, \'\');' + ); + page.expectFullQueryToContain( + 'DELETE FROM `disenchant_loot_template` WHERE (`Entry` = 1234);\n' + + 'INSERT INTO `disenchant_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, ' + + '`GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 0, 0, 100, 0, 1, 0, 1, 1, \'\');' + ); + + page.setInputValueById('Chance', '1'); + page.expectDiffQueryToContain( + 'DELETE FROM `disenchant_loot_template` WHERE (`Entry` = 1234) AND (`Item` IN (0));\n' + + 'INSERT INTO `disenchant_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, ' + + '`GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 0, 0, 1, 0, 1, 0, 1, 1, \'\');' + ); + page.expectFullQueryToContain( + 'DELETE FROM `disenchant_loot_template` WHERE (`Entry` = 1234);\n' + + 'INSERT INTO `disenchant_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, ' + + '`GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 0, 0, 1, 0, 1, 0, 1, 1, \'\');' + ); + + page.setInputValueById('QuestRequired', '2'); + page.expectDiffQueryToContain( + 'DELETE FROM `disenchant_loot_template` WHERE (`Entry` = 1234) AND (`Item` IN (0));\n' + + 'INSERT INTO `disenchant_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, ' + + '`GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 0, 0, 1, 2, 1, 0, 1, 1, \'\');' + ); + page.expectFullQueryToContain( + 'DELETE FROM `disenchant_loot_template` WHERE (`Entry` = 1234);\n' + + 'INSERT INTO `disenchant_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, ' + + '`GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 0, 0, 1, 2, 1, 0, 1, 1, \'\');' + ); + + page.setInputValueById('Item', '123'); + page.expectDiffQueryToContain( + 'DELETE FROM `disenchant_loot_template` WHERE (`Entry` = 1234) AND (`Item` IN (123));\n' + + 'INSERT INTO `disenchant_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, ' + + '`GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 123, 0, 1, 2, 1, 0, 1, 1, \'\');' + ); + page.expectFullQueryToContain( + 'DELETE FROM `disenchant_loot_template` WHERE (`Entry` = 1234);\n' + + 'INSERT INTO `disenchant_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, ' + + '`GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 123, 0, 1, 2, 1, 0, 1, 1, \'\');' + ); + }); + }); + + describe('Editing existing', () => { + beforeEach(() => setup(false)); + + it('should correctly initialise', () => { + expect(page.formError.hidden).toBe(true); + page.expectDiffQueryToBeShown(); + page.expectDiffQueryToBeEmpty(); + page.expectFullQueryToContain('' + + 'DELETE FROM `disenchant_loot_template` WHERE (`Entry` = 1234);\n' + + 'INSERT INTO `disenchant_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, ' + + '`GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 0, 0, 100, 0, 1, 0, 1, 1, \'\'),\n' + + '(1234, 1, 0, 100, 0, 1, 0, 1, 1, \'\'),\n' + + '(1234, 2, 0, 100, 0, 1, 0, 1, 1, \'\');'); + expect(page.getEditorTableRowsCount()).toBe(3); + }); + + it('deleting rows should correctly work', () => { + page.deleteRow(1); + expect(page.getEditorTableRowsCount()).toBe(2); + page.expectDiffQueryToContain( + 'DELETE FROM `disenchant_loot_template` WHERE (`Entry` = 1234) AND (`Item` IN (1));' + ); + page.expectFullQueryToContain( + 'DELETE FROM `disenchant_loot_template` WHERE (`Entry` = 1234);\n' + + 'INSERT INTO `disenchant_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, `GroupId`, ' + + '`MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 0, 0, 100, 0, 1, 0, 1, 1, \'\'),\n' + + '(1234, 2, 0, 100, 0, 1, 0, 1, 1, \'\');' + ); + + page.deleteRow(1); + expect(page.getEditorTableRowsCount()).toBe(1); + page.expectDiffQueryToContain( + 'DELETE FROM `disenchant_loot_template` WHERE (`Entry` = 1234) AND (`Item` IN (1, 2));' + ); + page.expectFullQueryToContain( + 'DELETE FROM `disenchant_loot_template` WHERE (`Entry` = 1234);\n' + + 'INSERT INTO `disenchant_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, `GroupId`, ' + + '`MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 0, 0, 100, 0, 1, 0, 1, 1, \'\');' + ); + + page.deleteRow(0); + expect(page.getEditorTableRowsCount()).toBe(0); + page.expectDiffQueryToContain( + 'DELETE FROM `disenchant_loot_template` WHERE `Entry` = 1234;' + ); + page.expectFullQueryToBeEmpty(); + }); + + it('editing existing rows should correctly work', () => { + page.clickRowOfDatatable(1); + page.setInputValueById('LootMode', 1); + + page.clickRowOfDatatable(2); + page.setInputValueById('GroupId', 2); + + page.expectDiffQueryToContain( + 'DELETE FROM `disenchant_loot_template` WHERE (`Entry` = 1234) AND (`Item` IN (2));\n' + + 'INSERT INTO `disenchant_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, `GroupId`, ' + + '`MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 2, 0, 100, 0, 1, 2, 1, 1, \'\');' + ); + page.expectFullQueryToContain( + 'DELETE FROM `disenchant_loot_template` WHERE (`Entry` = 1234);\n' + + 'INSERT INTO `disenchant_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, `GroupId`, ' + + '`MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 0, 0, 100, 0, 1, 0, 1, 1, \'\'),\n' + + '(1234, 1, 0, 100, 0, 1, 0, 1, 1, \'\'),\n' + + '(1234, 2, 0, 100, 0, 1, 2, 1, 1, \'\');' + ); + }); + + it('combining add, edit and delete should correctly work', () => { + page.addNewRow(); + expect(page.getEditorTableRowsCount()).toBe(4); + + page.clickRowOfDatatable(1); + page.setInputValueById('Chance', 10); + expect(page.getEditorTableRowsCount()).toBe(4); + + page.deleteRow(2); + expect(page.getEditorTableRowsCount()).toBe(3); + + page.expectDiffQueryToContain( + 'DELETE FROM `disenchant_loot_template` WHERE (`Entry` = 1234) AND (`Item` IN (1, 2, 3));\n' + + 'INSERT INTO `disenchant_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, `GroupId`, ' + + '`MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 1, 0, 10, 0, 1, 0, 1, 1, \'\'),\n' + + '(1234, 3, 0, 100, 0, 1, 0, 1, 1, \'\');' + ); + page.expectFullQueryToContain( + 'DELETE FROM `disenchant_loot_template` WHERE (`Entry` = 1234);\n' + + 'INSERT INTO `disenchant_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, `GroupId`, ' + + '`MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 0, 0, 100, 0, 1, 0, 1, 1, \'\'),\n' + + '(1234, 1, 0, 10, 0, 1, 0, 1, 1, \'\'),\n' + + '(1234, 3, 0, 100, 0, 1, 0, 1, 1, \'\');' + ); + }); + + it('using the same row id for multiple rows should correctly show an error', () => { + page.clickRowOfDatatable(2); + page.setInputValueById('Item', 0); + + page.expectUniqueError(); + }); + }); + + it('should correctly show the warning if the loot id is not correctly set in the item template', () => { + setup(true, 0); + + expect(page.query('.alert-info').innerText).toContain( + 'You have to set the field `DisenchantID` of item_template in order to enable this feature.' + ); + }); +}); diff --git a/src/app/components/editors/item/disenchant-loot-template/disenchant-loot-template.module.ts b/src/app/components/editors/item/disenchant-loot-template/disenchant-loot-template.module.ts new file mode 100644 index 00000000000..937c6dd41db --- /dev/null +++ b/src/app/components/editors/item/disenchant-loot-template/disenchant-loot-template.module.ts @@ -0,0 +1,31 @@ +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { ReactiveFormsModule } from '@angular/forms'; + +import { TopBarModule } from '../../shared/top-bar/top-bar.module'; +import { QueryOutputModule } from '../../shared/query-output/query-output.module'; +import { DisenchantLootTemplateComponent } from './disenchant-loot-template.component'; +import { ItemSelectorModule } from '../../shared/selectors/item-selector/item-selector.module'; +import { TooltipModule } from 'ngx-bootstrap'; +import { FlagsSelectorModule } from '../../shared/selectors/flags-selector/flags-selector.module'; +import { NgxDatatableModule } from '@swimlane/ngx-datatable'; + +@NgModule({ + declarations: [ + DisenchantLootTemplateComponent, + ], + imports: [ + BrowserModule, + ReactiveFormsModule, + TopBarModule, + QueryOutputModule, + TooltipModule.forRoot(), + ItemSelectorModule, + FlagsSelectorModule, + NgxDatatableModule, + ], + exports: [ + DisenchantLootTemplateComponent, + ], +}) +export class DisenchantLootTemplateModule {} diff --git a/src/app/components/editors/item/item-enchantment/item-enchantment-template.component.html b/src/app/components/editors/item/item-enchantment/item-enchantment-template.component.html new file mode 100644 index 00000000000..d05a7e894bf --- /dev/null +++ b/src/app/components/editors/item/item-enchantment/item-enchantment-template.component.html @@ -0,0 +1,81 @@ + + +
+ + Loading... + +
+ + + +
+ + + +
+ +
+
+ + +
+
+ + +
+ +
+ + The {{ editorService.entitySecondIdField }} must be unique. +
+ +
+ +
+ +
+
+ + + + + + + + + + + + + + + +
+
+
+
diff --git a/src/app/components/editors/item/item-enchantment/item-enchantment-template.component.scss b/src/app/components/editors/item/item-enchantment/item-enchantment-template.component.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/app/components/editors/item/item-enchantment/item-enchantment-template.component.ts b/src/app/components/editors/item/item-enchantment/item-enchantment-template.component.ts new file mode 100644 index 00000000000..aa885b7d0a6 --- /dev/null +++ b/src/app/components/editors/item/item-enchantment/item-enchantment-template.component.ts @@ -0,0 +1,22 @@ +import { Component } from '@angular/core'; + +import { MultiRowEditorComponent } from '../../shared/multi-row-editor.component'; +import { ItemHandlerService } from '../../../../services/handlers/item-handler.service'; +import { ItemEnchantmentTemplate } from '../../../../types/item-enchantment-template.type'; +import { ItemEnchantmentTemplateService } from '../../../../services/editors/item/item-enchantment-template.service'; + +@Component({ + selector: 'app-item-enchantment-template', + templateUrl: './item-enchantment-template.component.html', + styleUrls: ['./item-enchantment-template.component.scss'] +}) +export class ItemEnchantmentTemplateComponent extends MultiRowEditorComponent { + + /* istanbul ignore next */ // because of: https://github.com/gotwarlost/istanbul/issues/690 + constructor( + public editorService: ItemEnchantmentTemplateService, + public handlerService: ItemHandlerService, + ) { + super(editorService, handlerService); + } +} diff --git a/src/app/components/editors/item/item-enchantment/item-enchantment-template.integration.spec.ts b/src/app/components/editors/item/item-enchantment/item-enchantment-template.integration.spec.ts new file mode 100644 index 00000000000..1537e420a6e --- /dev/null +++ b/src/app/components/editors/item/item-enchantment/item-enchantment-template.integration.spec.ts @@ -0,0 +1,234 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { RouterTestingModule } from '@angular/router/testing'; +import { of } from 'rxjs'; +import Spy = jasmine.Spy; + +import { ItemEnchantmentTemplateComponent } from './item-enchantment-template.component'; +import { ItemEnchantmentTemplateModule } from './item-enchantment-template.module'; +import { QueryService } from '../../../../services/query.service'; +import { ItemEnchantmentTemplate } from '../../../../types/item-enchantment-template.type'; +import { ItemHandlerService } from '../../../../services/handlers/item-handler.service'; +import { MultiRowEditorPageObject } from '../../../../test-utils/multi-row-editor-page-object'; + +class ItemEnchantmentTemplatePage extends MultiRowEditorPageObject {} + +describe('ItemEnchantmentTemplate integration tests', () => { + let component: ItemEnchantmentTemplateComponent; + let fixture: ComponentFixture; + let queryService: QueryService; + let querySpy: Spy; + let handlerService: ItemHandlerService; + let page: ItemEnchantmentTemplatePage; + + const id = 1234; + + const originalRow0 = new ItemEnchantmentTemplate(); + const originalRow1 = new ItemEnchantmentTemplate(); + const originalRow2 = new ItemEnchantmentTemplate(); + originalRow0.entry = originalRow1.entry = originalRow2.entry = id; + originalRow0.ench = 0; + originalRow1.ench = 1; + originalRow2.ench = 2; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ + ItemEnchantmentTemplateModule, + RouterTestingModule, + ], + }) + .compileComponents(); + })); + + function setup(creatingNew: boolean) { + handlerService = TestBed.get(ItemHandlerService); + handlerService['_selected'] = `${id}`; + handlerService.isNew = creatingNew; + + queryService = TestBed.get(QueryService); + querySpy = spyOn(queryService, 'query').and.returnValue(of()); + + spyOn(queryService, 'selectAll').and.returnValue(of( + { results: creatingNew ? [] : [originalRow0, originalRow1, originalRow2] } + )); + + fixture = TestBed.createComponent(ItemEnchantmentTemplateComponent); + component = fixture.componentInstance; + page = new ItemEnchantmentTemplatePage(fixture); + fixture.autoDetectChanges(true); + fixture.detectChanges(); + } + + describe('Creating new', () => { + beforeEach(() => setup(true)); + + it('should correctly initialise', () => { + page.expectDiffQueryToBeEmpty(); + page.expectFullQueryToBeEmpty(); + expect(page.formError.hidden).toBe(true); + expect(page.addNewRowBtn.disabled).toBe(false); + expect(page.deleteSelectedRowBtn.disabled).toBe(true); + expect(page.getInputById('chance').disabled).toBe(true); + expect(page.getEditorTableRowsCount()).toBe(0); + }); + + it('adding new rows and executing the query should correctly work', () => { + const expectedQuery = 'DELETE FROM `item_enchantment_template` WHERE (`entry` = 1234) AND (`ench` IN (0, 1, 2));\n' + + 'INSERT INTO `item_enchantment_template` (`entry`, `ench`, `chance`) VALUES\n' + + '(1234, 0, 0),\n' + + '(1234, 1, 0),\n' + + '(1234, 2, 0);'; + querySpy.calls.reset(); + + page.addNewRow(); + expect(page.getEditorTableRowsCount()).toBe(1); + page.addNewRow(); + expect(page.getEditorTableRowsCount()).toBe(2); + page.addNewRow(); + expect(page.getEditorTableRowsCount()).toBe(3); + page.clickExecuteQuery(); + + page.expectDiffQueryToContain(expectedQuery); + expect(querySpy).toHaveBeenCalledTimes(1); + expect(querySpy.calls.mostRecent().args[0]).toContain(expectedQuery); + }); + + it('adding a row and changing its values should correctly update the queries', () => { + page.addNewRow(); + page.expectDiffQueryToContain( + 'DELETE FROM `item_enchantment_template` WHERE (`entry` = 1234) AND (`ench` IN (0));\n' + + 'INSERT INTO `item_enchantment_template` (`entry`, `ench`, `chance`) VALUES\n' + + '(1234, 0, 0);' + ); + page.expectFullQueryToContain( + 'DELETE FROM `item_enchantment_template` WHERE (`entry` = 1234);\n' + + 'INSERT INTO `item_enchantment_template` (`entry`, `ench`, `chance`) VALUES\n' + + '(1234, 0, 0);' + ); + + page.setInputValueById('chance', '1'); + page.expectDiffQueryToContain( + 'DELETE FROM `item_enchantment_template` WHERE (`entry` = 1234) AND (`ench` IN (0));\n' + + 'INSERT INTO `item_enchantment_template` (`entry`, `ench`, `chance`) VALUES\n' + + '(1234, 0, 1);' + ); + page.expectFullQueryToContain( + 'DELETE FROM `item_enchantment_template` WHERE (`entry` = 1234);\n' + + 'INSERT INTO `item_enchantment_template` (`entry`, `ench`, `chance`) VALUES\n' + + '(1234, 0, 1);' + ); + + page.setInputValueById('ench', '123'); + page.expectDiffQueryToContain( + 'DELETE FROM `item_enchantment_template` WHERE (`entry` = 1234) AND (`ench` IN (123));\n' + + 'INSERT INTO `item_enchantment_template` (`entry`, `ench`, `chance`) VALUES\n' + + '(1234, 123, 1);' + ); + page.expectFullQueryToContain( + 'DELETE FROM `item_enchantment_template` WHERE (`entry` = 1234);\n' + + 'INSERT INTO `item_enchantment_template` (`entry`, `ench`, `chance`) VALUES\n' + + '(1234, 123, 1);' + ); + }); + }); + + describe('Editing existing', () => { + beforeEach(() => setup(false)); + + it('should correctly initialise', () => { + expect(page.formError.hidden).toBe(true); + page.expectDiffQueryToBeShown(); + page.expectDiffQueryToBeEmpty(); + page.expectFullQueryToContain('DELETE FROM `item_enchantment_template` WHERE (`entry` = 1234);\n' + + 'INSERT INTO `item_enchantment_template` (`entry`, `ench`, `chance`) VALUES\n' + + '(1234, 0, 0),\n' + + '(1234, 1, 0),\n' + + '(1234, 2, 0);'); + expect(page.getEditorTableRowsCount()).toBe(3); + }); + + it('deleting rows should correctly work', () => { + page.deleteRow(1); + expect(page.getEditorTableRowsCount()).toBe(2); + page.expectDiffQueryToContain( + 'DELETE FROM `item_enchantment_template` WHERE (`entry` = 1234) AND (`ench` IN (1));' + ); + page.expectFullQueryToContain( + 'DELETE FROM `item_enchantment_template` WHERE (`entry` = 1234);\n' + + 'INSERT INTO `item_enchantment_template` (`entry`, `ench`, `chance`) VALUES\n' + + '(1234, 0, 0),\n' + + '(1234, 2, 0);' + ); + + page.deleteRow(1); + expect(page.getEditorTableRowsCount()).toBe(1); + page.expectDiffQueryToContain( + 'DELETE FROM `item_enchantment_template` WHERE (`entry` = 1234) AND (`ench` IN (1, 2));' + ); + page.expectFullQueryToContain( + 'DELETE FROM `item_enchantment_template` WHERE (`entry` = 1234);\n' + + 'INSERT INTO `item_enchantment_template` (`entry`, `ench`, `chance`) VALUES\n' + + '(1234, 0, 0);' + ); + + page.deleteRow(0); + expect(page.getEditorTableRowsCount()).toBe(0); + page.expectDiffQueryToContain( + 'DELETE FROM `item_enchantment_template` WHERE `entry` = 1234;' + ); + page.expectFullQueryToBeEmpty(); + }); + + it('editing existing rows should correctly work', () => { + page.clickRowOfDatatable(1); + page.setInputValueById('chance', 1); + + page.expectDiffQueryToContain( + 'DELETE FROM `item_enchantment_template` WHERE (`entry` = 1234) AND (`ench` IN (1));\n' + + 'INSERT INTO `item_enchantment_template` (`entry`, `ench`, `chance`) VALUES\n' + + '(1234, 1, 1);' + ); + page.expectFullQueryToContain( + 'DELETE FROM `item_enchantment_template` WHERE (`entry` = 1234);\n' + + 'INSERT INTO `item_enchantment_template` (`entry`, `ench`, `chance`) VALUES\n' + + '(1234, 0, 0),\n' + + '(1234, 1, 1),\n' + + '(1234, 2, 0);' + ); + }); + + it('combining add, edit and delete should correctly work', () => { + page.addNewRow(); + expect(page.getEditorTableRowsCount()).toBe(4); + + page.clickRowOfDatatable(1); + page.setInputValueById('chance', 10); + expect(page.getEditorTableRowsCount()).toBe(4); + + page.deleteRow(2); + expect(page.getEditorTableRowsCount()).toBe(3); + + page.expectDiffQueryToContain( + 'DELETE FROM `item_enchantment_template` WHERE (`entry` = 1234) AND (`ench` IN (1, 2, 3));\n' + + 'INSERT INTO `item_enchantment_template` (`entry`, `ench`, `chance`) VALUES\n' + + '(1234, 1, 10),\n' + + '(1234, 3, 0);' + ); + page.expectFullQueryToContain( + 'DELETE FROM `item_enchantment_template` WHERE (`entry` = 1234);\n' + + 'INSERT INTO `item_enchantment_template` (`entry`, `ench`, `chance`) VALUES\n' + + '(1234, 0, 0),\n' + + '(1234, 1, 10),\n' + + '(1234, 3, 0);' + ); + }); + + it('using the same row id for multiple rows should correctly show an error', () => { + page.clickRowOfDatatable(2); + page.setInputValueById('ench', 0); + + page.expectUniqueError(); + }); + }); +}); + diff --git a/src/app/components/editors/item/item-enchantment/item-enchantment-template.module.ts b/src/app/components/editors/item/item-enchantment/item-enchantment-template.module.ts new file mode 100644 index 00000000000..c603a965b0b --- /dev/null +++ b/src/app/components/editors/item/item-enchantment/item-enchantment-template.module.ts @@ -0,0 +1,25 @@ +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { ReactiveFormsModule } from '@angular/forms'; +import { NgxDatatableModule } from '@swimlane/ngx-datatable'; + +import { TopBarModule } from '../../shared/top-bar/top-bar.module'; +import { QueryOutputModule } from '../../shared/query-output/query-output.module'; +import { ItemEnchantmentTemplateComponent } from './item-enchantment-template.component'; + +@NgModule({ + declarations: [ + ItemEnchantmentTemplateComponent, + ], + imports: [ + BrowserModule, + ReactiveFormsModule, + TopBarModule, + QueryOutputModule, + NgxDatatableModule, + ], + exports: [ + ItemEnchantmentTemplateComponent, + ], +}) +export class ItemEnchantmentTemplateModule {} diff --git a/src/app/components/editors/item/item-loot-template/item-loot-template.component.html b/src/app/components/editors/item/item-loot-template/item-loot-template.component.html new file mode 100644 index 00000000000..288feb447f2 --- /dev/null +++ b/src/app/components/editors/item/item-loot-template/item-loot-template.component.html @@ -0,0 +1,152 @@ + + +
+ + Loading... + +
+ + + +
+ + + +
+ +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+ +
+ + The {{ editorService.entitySecondIdField }} must be unique. +
+ +
+ +
+ +
+
+ + + + + + + + + + + + + + + + + + + + + +
+
+
+
diff --git a/src/app/components/editors/item/item-loot-template/item-loot-template.component.scss b/src/app/components/editors/item/item-loot-template/item-loot-template.component.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/app/components/editors/item/item-loot-template/item-loot-template.component.ts b/src/app/components/editors/item/item-loot-template/item-loot-template.component.ts new file mode 100644 index 00000000000..4915a0d62c1 --- /dev/null +++ b/src/app/components/editors/item/item-loot-template/item-loot-template.component.ts @@ -0,0 +1,25 @@ +import { Component } from '@angular/core'; + +import { ItemHandlerService } from '../../../../services/handlers/item-handler.service'; +import { ItemLootTemplate } from '../../../../types/item-loot-template.type'; +import { ItemLootTemplateService } from '../../../../services/editors/item/item-loot-template.service'; +import { MultiRowEditorComponent } from '../../shared/multi-row-editor.component'; +import { LOOT_MODE } from '../../../../constants/flags/loot-mode'; + +@Component({ + selector: 'app-item-loot-template', + templateUrl: './item-loot-template.component.html', + styleUrls: ['./item-loot-template.component.scss'] +}) +export class ItemLootTemplateComponent extends MultiRowEditorComponent { + + public readonly LOOT_MODE = LOOT_MODE; + + /* istanbul ignore next */ // because of: https://github.com/gotwarlost/istanbul/issues/690 + constructor( + public editorService: ItemLootTemplateService, + public handlerService: ItemHandlerService, + ) { + super(editorService, handlerService); + } +} diff --git a/src/app/components/editors/item/item-loot-template/item-loot-template.integration.spec.ts b/src/app/components/editors/item/item-loot-template/item-loot-template.integration.spec.ts new file mode 100644 index 00000000000..966f4bd9c89 --- /dev/null +++ b/src/app/components/editors/item/item-loot-template/item-loot-template.integration.spec.ts @@ -0,0 +1,274 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { RouterTestingModule } from '@angular/router/testing'; +import { of } from 'rxjs'; +import Spy = jasmine.Spy; + +import { ItemLootTemplateComponent } from './item-loot-template.component'; +import { ItemLootTemplateModule } from './item-loot-template.module'; +import { QueryService } from '../../../../services/query.service'; +import { ItemLootTemplate } from '../../../../types/item-loot-template.type'; +import { ItemHandlerService } from '../../../../services/handlers/item-handler.service'; +import { MultiRowEditorPageObject } from '../../../../test-utils/multi-row-editor-page-object'; + +class ItemLootTemplatePage extends MultiRowEditorPageObject {} + +describe('ItemLootTemplate integration tests', () => { + let component: ItemLootTemplateComponent; + let fixture: ComponentFixture; + let queryService: QueryService; + let querySpy: Spy; + let handlerService: ItemHandlerService; + let page: ItemLootTemplatePage; + + const id = 1234; + + const originalRow0 = new ItemLootTemplate(); + const originalRow1 = new ItemLootTemplate(); + const originalRow2 = new ItemLootTemplate(); + originalRow0.Entry = originalRow1.Entry = originalRow2.Entry = id; + originalRow0.Item = 0; + originalRow1.Item = 1; + originalRow2.Item = 2; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ + ItemLootTemplateModule, + RouterTestingModule, + ], + }) + .compileComponents(); + })); + + function setup(creatingNew: boolean) { + handlerService = TestBed.get(ItemHandlerService); + handlerService['_selected'] = `${id}`; + handlerService.isNew = creatingNew; + + queryService = TestBed.get(QueryService); + querySpy = spyOn(queryService, 'query').and.returnValue(of()); + + spyOn(queryService, 'selectAll').and.returnValue(of( + { results: creatingNew ? [] : [originalRow0, originalRow1, originalRow2] } + )); + + fixture = TestBed.createComponent(ItemLootTemplateComponent); + component = fixture.componentInstance; + page = new ItemLootTemplatePage(fixture); + fixture.autoDetectChanges(true); + fixture.detectChanges(); + } + + describe('Creating new', () => { + beforeEach(() => setup(true)); + + it('should correctly initialise', () => { + page.expectDiffQueryToBeEmpty(); + page.expectFullQueryToBeEmpty(); + expect(page.formError.hidden).toBe(true); + expect(page.addNewRowBtn.disabled).toBe(false); + expect(page.deleteSelectedRowBtn.disabled).toBe(true); + expect(page.getInputById('Item').disabled).toBe(true); + expect(page.getInputById('Reference').disabled).toBe(true); + expect(page.getInputById('Chance').disabled).toBe(true); + expect(page.getInputById('QuestRequired').disabled).toBe(true); + expect(page.getInputById('LootMode').disabled).toBe(true); + expect(page.getInputById('GroupId').disabled).toBe(true); + expect(page.getInputById('MinCount').disabled).toBe(true); + expect(page.getInputById('MaxCount').disabled).toBe(true); + expect(page.getInputById('Comment').disabled).toBe(true); + expect(page.getEditorTableRowsCount()).toBe(0); + }); + + it('adding new rows and executing the query should correctly work', () => { + const expectedQuery = 'DELETE FROM `item_loot_template` WHERE (`Entry` = 1234) AND (`Item` IN (0, 1, 2));\n' + + 'INSERT INTO `item_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, `GroupId`, ' + + '`MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 0, 0, 100, 0, 1, 0, 1, 1, \'\'),\n' + + '(1234, 1, 0, 100, 0, 1, 0, 1, 1, \'\'),\n' + + '(1234, 2, 0, 100, 0, 1, 0, 1, 1, \'\');'; + querySpy.calls.reset(); + + page.addNewRow(); + expect(page.getEditorTableRowsCount()).toBe(1); + page.addNewRow(); + expect(page.getEditorTableRowsCount()).toBe(2); + page.addNewRow(); + expect(page.getEditorTableRowsCount()).toBe(3); + page.clickExecuteQuery(); + + page.expectDiffQueryToContain(expectedQuery); + expect(querySpy).toHaveBeenCalledTimes(1); + expect(querySpy.calls.mostRecent().args[0]).toContain(expectedQuery); + }); + + it('adding a row and changing its values should correctly update the queries', () => { + page.addNewRow(); + page.expectDiffQueryToContain( + 'DELETE FROM `item_loot_template` WHERE (`Entry` = 1234) AND (`Item` IN (0));\n' + + 'INSERT INTO `item_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, ' + + '`GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 0, 0, 100, 0, 1, 0, 1, 1, \'\');' + ); + page.expectFullQueryToContain( + 'DELETE FROM `item_loot_template` WHERE (`Entry` = 1234);\n' + + 'INSERT INTO `item_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, ' + + '`GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 0, 0, 100, 0, 1, 0, 1, 1, \'\');' + ); + + page.setInputValueById('Chance', '1'); + page.expectDiffQueryToContain( + 'DELETE FROM `item_loot_template` WHERE (`Entry` = 1234) AND (`Item` IN (0));\n' + + 'INSERT INTO `item_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, ' + + '`GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 0, 0, 1, 0, 1, 0, 1, 1, \'\');' + ); + page.expectFullQueryToContain( + 'DELETE FROM `item_loot_template` WHERE (`Entry` = 1234);\n' + + 'INSERT INTO `item_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, ' + + '`GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 0, 0, 1, 0, 1, 0, 1, 1, \'\');' + ); + + page.setInputValueById('QuestRequired', '2'); + page.expectDiffQueryToContain( + 'DELETE FROM `item_loot_template` WHERE (`Entry` = 1234) AND (`Item` IN (0));\n' + + 'INSERT INTO `item_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, ' + + '`GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 0, 0, 1, 2, 1, 0, 1, 1, \'\');' + ); + page.expectFullQueryToContain( + 'DELETE FROM `item_loot_template` WHERE (`Entry` = 1234);\n' + + 'INSERT INTO `item_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, ' + + '`GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 0, 0, 1, 2, 1, 0, 1, 1, \'\');' + ); + + page.setInputValueById('Item', '123'); + page.expectDiffQueryToContain( + 'DELETE FROM `item_loot_template` WHERE (`Entry` = 1234) AND (`Item` IN (123));\n' + + 'INSERT INTO `item_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, ' + + '`GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 123, 0, 1, 2, 1, 0, 1, 1, \'\');' + ); + page.expectFullQueryToContain( + 'DELETE FROM `item_loot_template` WHERE (`Entry` = 1234);\n' + + 'INSERT INTO `item_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, ' + + '`GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 123, 0, 1, 2, 1, 0, 1, 1, \'\');' + ); + }); + }); + + describe('Editing existing', () => { + beforeEach(() => setup(false)); + + it('should correctly initialise', () => { + expect(page.formError.hidden).toBe(true); + page.expectDiffQueryToBeShown(); + page.expectDiffQueryToBeEmpty(); + page.expectFullQueryToContain('' + + 'DELETE FROM `item_loot_template` WHERE (`Entry` = 1234);\n' + + 'INSERT INTO `item_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, ' + + '`GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 0, 0, 100, 0, 1, 0, 1, 1, \'\'),\n' + + '(1234, 1, 0, 100, 0, 1, 0, 1, 1, \'\'),\n' + + '(1234, 2, 0, 100, 0, 1, 0, 1, 1, \'\');'); + expect(page.getEditorTableRowsCount()).toBe(3); + }); + + it('deleting rows should correctly work', () => { + page.deleteRow(1); + expect(page.getEditorTableRowsCount()).toBe(2); + page.expectDiffQueryToContain( + 'DELETE FROM `item_loot_template` WHERE (`Entry` = 1234) AND (`Item` IN (1));' + ); + page.expectFullQueryToContain( + 'DELETE FROM `item_loot_template` WHERE (`Entry` = 1234);\n' + + 'INSERT INTO `item_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, `GroupId`, ' + + '`MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 0, 0, 100, 0, 1, 0, 1, 1, \'\'),\n' + + '(1234, 2, 0, 100, 0, 1, 0, 1, 1, \'\');' + ); + + page.deleteRow(1); + expect(page.getEditorTableRowsCount()).toBe(1); + page.expectDiffQueryToContain( + 'DELETE FROM `item_loot_template` WHERE (`Entry` = 1234) AND (`Item` IN (1, 2));' + ); + page.expectFullQueryToContain( + 'DELETE FROM `item_loot_template` WHERE (`Entry` = 1234);\n' + + 'INSERT INTO `item_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, `GroupId`, ' + + '`MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 0, 0, 100, 0, 1, 0, 1, 1, \'\');' + ); + + page.deleteRow(0); + expect(page.getEditorTableRowsCount()).toBe(0); + page.expectDiffQueryToContain( + 'DELETE FROM `item_loot_template` WHERE `Entry` = 1234;' + ); + page.expectFullQueryToBeEmpty(); + }); + + it('editing existing rows should correctly work', () => { + page.clickRowOfDatatable(1); + page.setInputValueById('LootMode', 1); + + page.clickRowOfDatatable(2); + page.setInputValueById('GroupId', 2); + + page.expectDiffQueryToContain( + 'DELETE FROM `item_loot_template` WHERE (`Entry` = 1234) AND (`Item` IN (2));\n' + + 'INSERT INTO `item_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, `GroupId`, ' + + '`MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 2, 0, 100, 0, 1, 2, 1, 1, \'\');' + ); + page.expectFullQueryToContain( + 'DELETE FROM `item_loot_template` WHERE (`Entry` = 1234);\n' + + 'INSERT INTO `item_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, `GroupId`, ' + + '`MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 0, 0, 100, 0, 1, 0, 1, 1, \'\'),\n' + + '(1234, 1, 0, 100, 0, 1, 0, 1, 1, \'\'),\n' + + '(1234, 2, 0, 100, 0, 1, 2, 1, 1, \'\');' + ); + }); + + it('combining add, edit and delete should correctly work', () => { + page.addNewRow(); + expect(page.getEditorTableRowsCount()).toBe(4); + + page.clickRowOfDatatable(1); + page.setInputValueById('Chance', 10); + expect(page.getEditorTableRowsCount()).toBe(4); + + page.deleteRow(2); + expect(page.getEditorTableRowsCount()).toBe(3); + + page.expectDiffQueryToContain( + 'DELETE FROM `item_loot_template` WHERE (`Entry` = 1234) AND (`Item` IN (1, 2, 3));\n' + + 'INSERT INTO `item_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, `GroupId`, ' + + '`MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 1, 0, 10, 0, 1, 0, 1, 1, \'\'),\n' + + '(1234, 3, 0, 100, 0, 1, 0, 1, 1, \'\');' + ); + page.expectFullQueryToContain( + 'DELETE FROM `item_loot_template` WHERE (`Entry` = 1234);\n' + + 'INSERT INTO `item_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, `GroupId`, ' + + '`MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 0, 0, 100, 0, 1, 0, 1, 1, \'\'),\n' + + '(1234, 1, 0, 10, 0, 1, 0, 1, 1, \'\'),\n' + + '(1234, 3, 0, 100, 0, 1, 0, 1, 1, \'\');' + ); + }); + + it('using the same row id for multiple rows should correctly show an error', () => { + page.clickRowOfDatatable(2); + page.setInputValueById('Item', 0); + + page.expectUniqueError(); + }); + }); + +}); diff --git a/src/app/components/editors/item/item-loot-template/item-loot-template.module.ts b/src/app/components/editors/item/item-loot-template/item-loot-template.module.ts new file mode 100644 index 00000000000..0b331b399a6 --- /dev/null +++ b/src/app/components/editors/item/item-loot-template/item-loot-template.module.ts @@ -0,0 +1,31 @@ +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { ReactiveFormsModule } from '@angular/forms'; + +import { TopBarModule } from '../../shared/top-bar/top-bar.module'; +import { QueryOutputModule } from '../../shared/query-output/query-output.module'; +import { ItemLootTemplateComponent } from './item-loot-template.component'; +import { ItemSelectorModule } from '../../shared/selectors/item-selector/item-selector.module'; +import { TooltipModule } from 'ngx-bootstrap'; +import { FlagsSelectorModule } from '../../shared/selectors/flags-selector/flags-selector.module'; +import { NgxDatatableModule } from '@swimlane/ngx-datatable'; + +@NgModule({ + declarations: [ + ItemLootTemplateComponent, + ], + imports: [ + BrowserModule, + ReactiveFormsModule, + TopBarModule, + QueryOutputModule, + TooltipModule.forRoot(), + ItemSelectorModule, + FlagsSelectorModule, + NgxDatatableModule, + ], + exports: [ + ItemLootTemplateComponent, + ], +}) +export class ItemLootTemplateModule {} diff --git a/src/app/components/editors/item/item-template/item-template.component.html b/src/app/components/editors/item/item-template/item-template.component.html index a372f0304ce..de9ffc966d8 100644 --- a/src/app/components/editors/item/item-template/item-template.component.html +++ b/src/app/components/editors/item/item-template/item-template.component.html @@ -21,6 +21,759 @@ class="form-group edit-form" > +
+
+
+ + + +
+ +
+ + + +
+ +
+ + + +
+ +
+ + + +
+ +
+ +
+
+ + + +
+
+ + + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + +
+
+ + + +
+
+ + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + Requirements + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + Resistance + +
+
+ + +
+
+ + + Stats + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+
+ +
+
+
+ + + +
+
+ + + +
+
+
+ +
+ +
+
+ + Damage +
+ +
+
+
+ + + +
+
+ + + +
+
+ + + +
+
+
+ +
+ +
+
+ + + Socket + +
+
+ + + +
+
+ + + +
+
+
+ +
+
+
+ + + +
+
+ + + +
+
+
+ +
+ +
+
+ + + Weapon + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+ +
+ + + Spell + +
+ +
+
+
+ + +
+
+ + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+
+ +
+
diff --git a/src/app/components/editors/item/item-template/item-template.component.ts b/src/app/components/editors/item/item-template/item-template.component.ts index 7e9b7cb52c3..c06cf5e1d16 100644 --- a/src/app/components/editors/item/item-template/item-template.component.ts +++ b/src/app/components/editors/item/item-template/item-template.component.ts @@ -4,6 +4,26 @@ import { SingleRowEditorComponent } from '../../shared/single-row-editor.compone import { ItemTemplate } from '../../../../types/item-template.type'; import { ItemTemplateService } from '../../../../services/editors/item/item-template.service'; import { ItemHandlerService } from '../../../../services/handlers/item-handler.service'; +import { ITEM_CLASS, ITEM_SUBCLASS } from '../../../../constants/options/item-class'; +import { ITEM_QUALITY } from '../../../../constants/options/item-quality'; +import { ITEM_FLAGS } from '../../../../constants/flags/item-flags'; +import { ITEM_FLAGS_EXTRA } from '../../../../constants/flags/item-flags-extra'; +import { INVENTORY_TYPE } from '../../../../constants/options/inventory-type'; +import { ALLOWABLE_CLASSES } from '../../../../constants/flags/allowable-classes'; +import { ALLOWABLE_RACES } from '../../../../constants/flags/allowable-races'; +import { FACTION_RANK } from '../../../../constants/options/faction-rank'; +import { BAG_FAMILY } from '../../../../constants/flags/bag-family'; +import { SOCKET_COLOR } from '../../../../constants/flags/socket-color'; +import { ITEM_BONDING } from '../../../../constants/options/item-bonding'; +import { ITEM_MATERIAL } from '../../../../constants/options/item-material'; +import { ITEM_SHEAT } from '../../../../constants/options/item-sheath'; +import { TOTEM_CATEGORY } from '../../../../constants/options/totem-category'; +import { FOOD_TYPE } from '../../../../constants/options/foot-type'; +import { ITEM_FLAGS_CUSTOM } from '../../../../constants/flags/item-flags-custom'; +import { DAMAGE_TYPE } from '../../../../constants/options/damage-type'; +import { SOCKET_BONUS } from '../../../../constants/options/socket-bonus'; +import { FACTIONS } from '../../../../constants/options/faction'; +import { STAT_TYPE } from '../../../../constants/options/stat-type'; @Component({ selector: 'app-item-template', @@ -12,6 +32,28 @@ import { ItemHandlerService } from '../../../../services/handlers/item-handler.s }) export class ItemTemplateComponent extends SingleRowEditorComponent { + public readonly ITEM_CLASS = ITEM_CLASS; + public readonly ITEM_SUBCLASS = ITEM_SUBCLASS; + public readonly ITEM_QUALITY = ITEM_QUALITY; + public readonly ITEM_FLAGS = ITEM_FLAGS; + public readonly ITEM_FLAGS_EXTRA = ITEM_FLAGS_EXTRA; + public readonly INVENTORY_TYPE = INVENTORY_TYPE; + public readonly ALLOWABLE_CLASSES = ALLOWABLE_CLASSES; + public readonly ALLOWABLE_RACES = ALLOWABLE_RACES; + public readonly FACTION_RANK = FACTION_RANK; + public readonly BAG_FAMILY = BAG_FAMILY; + public readonly SOCKET_COLOR = SOCKET_COLOR; + public readonly ITEM_BONDING = ITEM_BONDING; + public readonly ITEM_MATERIAL = ITEM_MATERIAL; + public readonly ITEM_SHEAT = ITEM_SHEAT; + public readonly TOTEM_CATEGORY = TOTEM_CATEGORY; + public readonly FOOD_TYPE = FOOD_TYPE; + public readonly ITEM_FLAGS_CUSTOM = ITEM_FLAGS_CUSTOM; + public readonly DAMAGE_TYPE = DAMAGE_TYPE; + public readonly SOCKET_BONUS = SOCKET_BONUS; + public readonly FACTIONS = FACTIONS; + public readonly STAT_TYPE = STAT_TYPE; + /* istanbul ignore next */ // because of: https://github.com/gotwarlost/istanbul/issues/690 constructor( public editorService: ItemTemplateService, diff --git a/src/app/components/editors/item/item-template/item-template.integration.spec.ts b/src/app/components/editors/item/item-template/item-template.integration.spec.ts index e69de29bb2d..e7616b41d73 100644 --- a/src/app/components/editors/item/item-template/item-template.integration.spec.ts +++ b/src/app/components/editors/item/item-template/item-template.integration.spec.ts @@ -0,0 +1,214 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { RouterTestingModule } from '@angular/router/testing'; +import { of } from 'rxjs'; +import Spy = jasmine.Spy; + +import { ItemTemplateComponent } from './item-template.component'; +import { ItemTemplateModule } from './item-template.module'; +import { QueryService } from '../../../../services/query.service'; +import { EditorPageObject } from '../../../../test-utils/editor-page-object'; +import { ItemTemplate } from '../../../../types/item-template.type'; +import { ItemHandlerService } from '../../../../services/handlers/item-handler.service'; +import { ITEM_SUBCLASS } from '../../../../constants/options/item-class'; + +class ItemTemplatePage extends EditorPageObject {} + +describe('ItemTemplate integration tests', () => { + let component: ItemTemplateComponent; + let fixture: ComponentFixture; + let queryService: QueryService; + let querySpy: Spy; + let handlerService: ItemHandlerService; + let page: ItemTemplatePage; + + const id = 1234; + const expectedFullCreateQuery = 'DELETE FROM `item_template` WHERE (`entry` = 1234);\n' + + 'INSERT INTO `item_template` (`entry`, `class`, `subclass`, `SoundOverrideSubclass`, `name`, `displayid`, `Quality`, ' + + '`Flags`, `FlagsExtra`, `BuyCount`, `BuyPrice`, `SellPrice`, `InventoryType`, `AllowableClass`, `AllowableRace`, ' + + '`ItemLevel`, `RequiredLevel`, `RequiredSkill`, `RequiredSkillRank`, `requiredspell`, `requiredhonorrank`, ' + + '`RequiredCityRank`, `RequiredReputationFaction`, `RequiredReputationRank`, `maxcount`, `stackable`, `ContainerSlots`, ' + + '`StatsCount`, `stat_type1`, `stat_value1`, `stat_type2`, `stat_value2`, `stat_type3`, `stat_value3`, ' + + '`stat_type4`, `stat_value4`, `stat_type5`, `stat_value5`, `stat_type6`, `stat_value6`, `stat_type7`, `stat_value7`, ' + + '`stat_type8`, `stat_value8`, `stat_type9`, `stat_value9`, `stat_type10`, `stat_value10`, `ScalingStatDistribution`, ' + + '`ScalingStatValue`, `dmg_min1`, `dmg_max1`, `dmg_type1`, `dmg_min2`, `dmg_max2`, `dmg_type2`, `armor`, `holy_res`, ' + + '`fire_res`, `nature_res`, `frost_res`, `shadow_res`, `arcane_res`, `delay`, `ammo_type`, `RangedModRange`, `spellid_1`, ' + + '`spelltrigger_1`, `spellcharges_1`, `spellppmRate_1`, `spellcooldown_1`, `spellcategory_1`, `spellcategorycooldown_1`, ' + + '`spellid_2`, `spelltrigger_2`, `spellcharges_2`, `spellppmRate_2`, `spellcooldown_2`, `spellcategory_2`, ' + + '`spellcategorycooldown_2`, `spellid_3`, `spelltrigger_3`, `spellcharges_3`, `spellppmRate_3`, `spellcooldown_3`, ' + + '`spellcategory_3`, `spellcategorycooldown_3`, `spellid_4`, `spelltrigger_4`, `spellcharges_4`, `spellppmRate_4`, ' + + '`spellcooldown_4`, `spellcategory_4`, `spellcategorycooldown_4`, `spellid_5`, `spelltrigger_5`, `spellcharges_5`, ' + + '`spellppmRate_5`, `spellcooldown_5`, `spellcategory_5`, `spellcategorycooldown_5`, `bonding`, `description`, `PageText`, ' + + '`LanguageID`, `PageMaterial`, `startquest`, `lockid`, `Material`, `sheath`, `RandomProperty`, `RandomSuffix`, `block`, ' + + '`itemset`, `MaxDurability`, `area`, `Map`, `BagFamily`, `TotemCategory`, `socketColor_1`, `socketContent_1`, `socketColor_2`, ' + + '`socketContent_2`, `socketColor_3`, `socketContent_3`, `socketBonus`, `GemProperties`, `RequiredDisenchantSkill`, ' + + '`ArmorDamageModifier`, `duration`, `ItemLimitCategory`, `HolidayId`, `ScriptName`, `DisenchantID`, `FoodType`, ' + + '`minMoneyLoot`, `maxMoneyLoot`, `flagsCustom`, `VerifiedBuild`) VALUES\n' + + '(1234, 0, 0, -1, \'\', 0, 0, 0, 0, 1, 0, 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ' + + '0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1000, 0, 0, 0, 0, 0, 0, -1, 0, -1, 0, 0, 0, 0, -1, ' + + '0, -1, 0, 0, 0, 0, -1, 0, -1, 0, 0, 0, 0, -1, 0, -1, 0, 0, 0, 0, -1, 0, -1, 0, \'\', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ' + + '0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, \'\', 0, 0, 0, 0, 0, 0);\n'; + + const originalEntity = new ItemTemplate(); + originalEntity.entry = id; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ + ItemTemplateModule, + RouterTestingModule, + ], + }) + .compileComponents(); + })); + + function setup(creatingNew: boolean) { + handlerService = TestBed.get(ItemHandlerService); + handlerService['_selected'] = `${id}`; + handlerService.isNew = creatingNew; + + queryService = TestBed.get(QueryService); + querySpy = spyOn(queryService, 'query').and.returnValue(of()); + + spyOn(queryService, 'selectAll').and.returnValue(of( + { results: creatingNew ? [] : [originalEntity] } + )); + + fixture = TestBed.createComponent(ItemTemplateComponent); + component = fixture.componentInstance; + page = new ItemTemplatePage(fixture); + fixture.autoDetectChanges(true); + fixture.detectChanges(); + } + + describe('Creating new', () => { + beforeEach(() => setup(true)); + + it('should correctly initialise', () => { + page.expectQuerySwitchToBeHidden(); + page.expectFullQueryToBeShown(); + page.expectFullQueryToContain(expectedFullCreateQuery); + }); + + it('changing a property and executing the query should correctly work', () => { + querySpy.calls.reset(); + + page.setInputValueById('name', 'Shin'); + page.clickExecuteQuery(); + + // Note: full query check has been shortened here because the table is too big, don't do this in other tests unless necessary + page.expectFullQueryToContain('Shin'); + expect(querySpy).toHaveBeenCalledTimes(1); + expect(querySpy.calls.mostRecent().args[0]).toContain('Shin'); + }); + }); + + describe('Editing existing', () => { + beforeEach(() => setup(false)); + + it('should correctly initialise', () => { + page.expectDiffQueryToBeShown(); + page.expectDiffQueryToBeEmpty(); + page.expectFullQueryToContain(expectedFullCreateQuery); + }); + + it('changing all properties and executing the query should correctly work', () => { + const expectedQuery = 'UPDATE `item_template` SET ' + + '`subclass` = 1, `SoundOverrideSubclass` = 2, `name` = \'3\', `displayid` = 4, `Quality` = 5, `Flags` = 6, `FlagsExtra` = 7, ' + + '`BuyCount` = 8, `BuyPrice` = 9, `SellPrice` = 10, `InventoryType` = 11, `AllowableClass` = 12, `AllowableRace` = 13, ' + + '`ItemLevel` = 14, `RequiredLevel` = 15, `RequiredSkill` = 16, `RequiredSkillRank` = 17, `requiredspell` = 18, ' + + '`requiredhonorrank` = 19, `RequiredCityRank` = 20, `RequiredReputationFaction` = 21, `RequiredReputationRank` = 22, ' + + '`maxcount` = 23, `stackable` = 24, `ContainerSlots` = 25, `StatsCount` = 26, `stat_type1` = 27, `stat_value1` = 28, ' + + '`stat_type2` = 29, `stat_value2` = 30, `stat_type3` = 31, `stat_value3` = 32, `stat_type4` = 33, `stat_value4` = 34, ' + + '`stat_type5` = 35, `stat_value5` = 36, `stat_type6` = 37, `stat_value6` = 38, `stat_type7` = 39, `stat_value7` = 40, ' + + '`stat_type8` = 41, `stat_value8` = 42, `stat_type9` = 43, `stat_value9` = 44, `stat_type10` = 45, `stat_value10` = 46, ' + + '`ScalingStatDistribution` = 47, `ScalingStatValue` = 48, `dmg_min1` = 49, `dmg_max1` = 50, `dmg_type1` = 51, ' + + '`dmg_min2` = 52, `dmg_max2` = 53, `dmg_type2` = 54, `armor` = 55, `holy_res` = 56, `fire_res` = 57, `nature_res` = 58, ' + + '`frost_res` = 59, `shadow_res` = 60, `arcane_res` = 61, `delay` = 62, `ammo_type` = 63, `RangedModRange` = 64, ' + + '`spellid_1` = 65, `spelltrigger_1` = 66, `spellcharges_1` = 67, `spellppmRate_1` = 68, `spellcooldown_1` = 69, ' + + '`spellcategory_1` = 70, `spellcategorycooldown_1` = 71, `spellid_2` = 72, `spelltrigger_2` = 73, `spellcharges_2` = 74, ' + + '`spellppmRate_2` = 75, `spellcooldown_2` = 76, `spellcategory_2` = 77, `spellcategorycooldown_2` = 78, `spellid_3` = 79, ' + + '`spelltrigger_3` = 80, `spellcharges_3` = 81, `spellppmRate_3` = 82, `spellcooldown_3` = 83, `spellcategory_3` = 84, ' + + '`spellcategorycooldown_3` = 85, `spellid_4` = 86, `spelltrigger_4` = 87, `spellcharges_4` = 88, `spellppmRate_4` = 89, ' + + '`spellcooldown_4` = 90, `spellcategory_4` = 91, `spellcategorycooldown_4` = 92, `spellid_5` = 93, `spelltrigger_5` = 94, ' + + '`spellcharges_5` = 95, `spellppmRate_5` = 96, `spellcooldown_5` = 97, `spellcategory_5` = 98, ' + + '`spellcategorycooldown_5` = 99, `bonding` = 100, `description` = \'101\', `PageText` = 102, `LanguageID` = 103, ' + + '`PageMaterial` = 104, `startquest` = 105, `lockid` = 106, `Material` = 107, `sheath` = 108, `RandomProperty` = 109, ' + + '`RandomSuffix` = 110, `block` = 111, `itemset` = 112, `MaxDurability` = 113, `area` = 114, `Map` = 115, `BagFamily` = 116, ' + + '`TotemCategory` = 117, `socketColor_1` = 118, `socketContent_1` = 119, `socketColor_2` = 120, `socketContent_2` = 121, ' + + '`socketColor_3` = 122, `socketContent_3` = 123, `socketBonus` = 124, `GemProperties` = 125, `RequiredDisenchantSkill` = 126, ' + + '`ArmorDamageModifier` = 127, `duration` = 128, `ItemLimitCategory` = 129, `HolidayId` = 130, `ScriptName` = \'131\', ' + + '`DisenchantID` = 132, `FoodType` = 133, `minMoneyLoot` = 134, `maxMoneyLoot` = 135, `flagsCustom` = 136 WHERE (`entry` = 1234);'; + querySpy.calls.reset(); + + page.changeAllFields(originalEntity, ['VerifiedBuild']); + page.clickExecuteQuery(); + + page.expectDiffQueryToContain(expectedQuery); + expect(querySpy).toHaveBeenCalledTimes(1); + expect(querySpy.calls.mostRecent().args[0]).toContain(expectedQuery); + }); + + it('changing values should correctly update the queries', () => { + // Note: full query check has been shortened here because the table is too big, don't do this in other tests unless necessary + + page.setInputValueById('name', 'Shin'); + page.expectDiffQueryToContain( + 'UPDATE `item_template` SET `name` = \'Shin\' WHERE (`entry` = 1234);' + ); + page.expectFullQueryToContain('Shin'); + + page.setInputValueById('BuyCount', 22); + page.expectDiffQueryToContain( + 'UPDATE `item_template` SET `name` = \'Shin\', `BuyCount` = 22 WHERE (`entry` = 1234);' + ); + page.expectFullQueryToContain('Shin'); + page.expectFullQueryToContain('22'); + }); + + it('changing a value via FlagsSelector should correctly work', () => { + const field = 'Flags'; + page.clickElement(page.getSelectorBtn(field)); + page.expectModalDisplayed(); + + page.toggleFlagInRow(2); + page.toggleFlagInRow(12); + page.clickModalSelect(); + + expect(page.getInputById(field).value).toEqual('4100'); + page.expectDiffQueryToContain( + 'UPDATE `item_template` SET `Flags` = 4100 WHERE (`entry` = 1234);' + ); + + // Note: full query check has been shortened here because the table is too big, don't do this in other tests unless necessary + page.expectFullQueryToContain('4100'); + }); + + describe('the subclass field', () => { + it('should show the selector button only if class has a valid value', () => { + page.setInputValueById('class', 100); + expect(page.getSelectorBtn('subclass', false)).toBeFalsy(); + + page.setInputValueById('class', 0); + expect(page.getSelectorBtn('subclass', false)).toBeTruthy(); + + page.setInputValueById('class', -1); + expect(page.getSelectorBtn('subclass', false)).toBeFalsy(); + + page.setInputValueById('class', 10); + expect(page.getSelectorBtn('subclass', false)).toBeTruthy(); + + page.setInputValueById('class', null); + expect(page.getSelectorBtn('subclass', false)).toBeFalsy(); + }); + + it('should show its values according to the value of class', () => { + page.setInputValueById('class', 3); + page.clickElement(page.getSelectorBtn('subclass')); + + expect(page.getCellOfDatatableInModal(2, 1).innerText).toContain(ITEM_SUBCLASS[3][2].name); + page.clickModalSelect(); + }); + }); + }); +}); + diff --git a/src/app/components/editors/item/item-template/item-template.module.ts b/src/app/components/editors/item/item-template/item-template.module.ts index aeffa269e07..f12affafe7c 100644 --- a/src/app/components/editors/item/item-template/item-template.module.ts +++ b/src/app/components/editors/item/item-template/item-template.module.ts @@ -8,7 +8,6 @@ import { QueryOutputModule } from '../../shared/query-output/query-output.module import { ItemTemplateComponent } from './item-template.component'; import { SingleValueSelectorModule } from '../../shared/selectors/single-value-selector/single-value-selector.module'; import { FlagsSelectorModule } from '../../shared/selectors/flags-selector/flags-selector.module'; -import { ItemSelectorModule } from '../../shared/selectors/item-selector/item-selector.module'; @NgModule({ declarations: [ @@ -22,7 +21,6 @@ import { ItemSelectorModule } from '../../shared/selectors/item-selector/item-se TooltipModule.forRoot(), SingleValueSelectorModule, FlagsSelectorModule, - ItemSelectorModule, ], exports: [ ItemTemplateComponent, diff --git a/src/app/components/editors/item/item.module.ts b/src/app/components/editors/item/item.module.ts index 5d86b6a28e9..fec9ac9887f 100644 --- a/src/app/components/editors/item/item.module.ts +++ b/src/app/components/editors/item/item.module.ts @@ -1,10 +1,20 @@ import { NgModule } from '@angular/core'; import { ItemTemplateModule } from './item-template/item-template.module'; import { SelectItemModule } from './select-item/select-item.module'; +import { ItemLootTemplateModule } from './item-loot-template/item-loot-template.module'; +import { DisenchantLootTemplateModule } from './disenchant-loot-template/disenchant-loot-template.module'; +import { ProspectingLootTemplateModule } from './prospecting-loot-template/prospecting-loot-template.module'; +import { MillingLootTemplateModule } from './milling-loot-template/milling-loot-template.module'; +import { ItemEnchantmentTemplateModule } from './item-enchantment/item-enchantment-template.module'; const modules = [ SelectItemModule, ItemTemplateModule, + ItemEnchantmentTemplateModule, + ItemLootTemplateModule, + DisenchantLootTemplateModule, + ProspectingLootTemplateModule, + MillingLootTemplateModule ]; @NgModule({ diff --git a/src/app/components/editors/item/milling-loot-template/milling-loot-template.component.ts b/src/app/components/editors/item/milling-loot-template/milling-loot-template.component.ts new file mode 100644 index 00000000000..79229e8f9eb --- /dev/null +++ b/src/app/components/editors/item/milling-loot-template/milling-loot-template.component.ts @@ -0,0 +1,25 @@ +import { Component } from '@angular/core'; + +import { ItemHandlerService } from '../../../../services/handlers/item-handler.service'; +import { MillingLootTemplate } from '../../../../types/milling-loot-template.type'; +import { MillingLootTemplateService } from '../../../../services/editors/item/milling-loot-template.service'; +import { MultiRowEditorComponent } from '../../shared/multi-row-editor.component'; +import { LOOT_MODE } from '../../../../constants/flags/loot-mode'; + +@Component({ + selector: 'app-milling-loot-template', + templateUrl: '../item-loot-template/item-loot-template.component.html', + styleUrls: ['../item-loot-template/item-loot-template.component.scss'] +}) +export class MillingLootTemplateComponent extends MultiRowEditorComponent { + + public readonly LOOT_MODE = LOOT_MODE; + + /* istanbul ignore next */ // because of: https://github.com/gotwarlost/istanbul/issues/690 + constructor( + public editorService: MillingLootTemplateService, + public handlerService: ItemHandlerService, + ) { + super(editorService, handlerService); + } +} diff --git a/src/app/components/editors/item/milling-loot-template/milling-loot-template.integration.spec.ts b/src/app/components/editors/item/milling-loot-template/milling-loot-template.integration.spec.ts new file mode 100644 index 00000000000..de6ce21c28c --- /dev/null +++ b/src/app/components/editors/item/milling-loot-template/milling-loot-template.integration.spec.ts @@ -0,0 +1,274 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { RouterTestingModule } from '@angular/router/testing'; +import { of } from 'rxjs'; +import Spy = jasmine.Spy; + +import { MillingLootTemplateComponent } from './milling-loot-template.component'; +import { MillingLootTemplateModule } from './milling-loot-template.module'; +import { QueryService } from '../../../../services/query.service'; +import { MillingLootTemplate } from '../../../../types/milling-loot-template.type'; +import { ItemHandlerService } from '../../../../services/handlers/item-handler.service'; +import { MultiRowEditorPageObject } from '../../../../test-utils/multi-row-editor-page-object'; + +class MillingLootTemplatePage extends MultiRowEditorPageObject {} + +describe('MillingLootTemplate integration tests', () => { + let component: MillingLootTemplateComponent; + let fixture: ComponentFixture; + let queryService: QueryService; + let querySpy: Spy; + let handlerService: ItemHandlerService; + let page: MillingLootTemplatePage; + + const id = 1234; + + const originalRow0 = new MillingLootTemplate(); + const originalRow1 = new MillingLootTemplate(); + const originalRow2 = new MillingLootTemplate(); + originalRow0.Entry = originalRow1.Entry = originalRow2.Entry = id; + originalRow0.Item = 0; + originalRow1.Item = 1; + originalRow2.Item = 2; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ + MillingLootTemplateModule, + RouterTestingModule, + ], + }) + .compileComponents(); + })); + + function setup(creatingNew: boolean) { + handlerService = TestBed.get(ItemHandlerService); + handlerService['_selected'] = `${id}`; + handlerService.isNew = creatingNew; + + queryService = TestBed.get(QueryService); + querySpy = spyOn(queryService, 'query').and.returnValue(of()); + + spyOn(queryService, 'selectAll').and.returnValue(of( + { results: creatingNew ? [] : [originalRow0, originalRow1, originalRow2] } + )); + + fixture = TestBed.createComponent(MillingLootTemplateComponent); + component = fixture.componentInstance; + page = new MillingLootTemplatePage(fixture); + fixture.autoDetectChanges(true); + fixture.detectChanges(); + } + + describe('Creating new', () => { + beforeEach(() => setup(true)); + + it('should correctly initialise', () => { + page.expectDiffQueryToBeEmpty(); + page.expectFullQueryToBeEmpty(); + expect(page.formError.hidden).toBe(true); + expect(page.addNewRowBtn.disabled).toBe(false); + expect(page.deleteSelectedRowBtn.disabled).toBe(true); + expect(page.getInputById('Item').disabled).toBe(true); + expect(page.getInputById('Reference').disabled).toBe(true); + expect(page.getInputById('Chance').disabled).toBe(true); + expect(page.getInputById('QuestRequired').disabled).toBe(true); + expect(page.getInputById('LootMode').disabled).toBe(true); + expect(page.getInputById('GroupId').disabled).toBe(true); + expect(page.getInputById('MinCount').disabled).toBe(true); + expect(page.getInputById('MaxCount').disabled).toBe(true); + expect(page.getInputById('Comment').disabled).toBe(true); + expect(page.getEditorTableRowsCount()).toBe(0); + }); + + it('adding new rows and executing the query should correctly work', () => { + const expectedQuery = 'DELETE FROM `milling_loot_template` WHERE (`Entry` = 1234) AND (`Item` IN (0, 1, 2));\n' + + 'INSERT INTO `milling_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, `GroupId`, ' + + '`MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 0, 0, 100, 0, 1, 0, 1, 1, \'\'),\n' + + '(1234, 1, 0, 100, 0, 1, 0, 1, 1, \'\'),\n' + + '(1234, 2, 0, 100, 0, 1, 0, 1, 1, \'\');'; + querySpy.calls.reset(); + + page.addNewRow(); + expect(page.getEditorTableRowsCount()).toBe(1); + page.addNewRow(); + expect(page.getEditorTableRowsCount()).toBe(2); + page.addNewRow(); + expect(page.getEditorTableRowsCount()).toBe(3); + page.clickExecuteQuery(); + + page.expectDiffQueryToContain(expectedQuery); + expect(querySpy).toHaveBeenCalledTimes(1); + expect(querySpy.calls.mostRecent().args[0]).toContain(expectedQuery); + }); + + it('adding a row and changing its values should correctly update the queries', () => { + page.addNewRow(); + page.expectDiffQueryToContain( + 'DELETE FROM `milling_loot_template` WHERE (`Entry` = 1234) AND (`Item` IN (0));\n' + + 'INSERT INTO `milling_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, ' + + '`GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 0, 0, 100, 0, 1, 0, 1, 1, \'\');' + ); + page.expectFullQueryToContain( + 'DELETE FROM `milling_loot_template` WHERE (`Entry` = 1234);\n' + + 'INSERT INTO `milling_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, ' + + '`GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 0, 0, 100, 0, 1, 0, 1, 1, \'\');' + ); + + page.setInputValueById('Chance', '1'); + page.expectDiffQueryToContain( + 'DELETE FROM `milling_loot_template` WHERE (`Entry` = 1234) AND (`Item` IN (0));\n' + + 'INSERT INTO `milling_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, ' + + '`GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 0, 0, 1, 0, 1, 0, 1, 1, \'\');' + ); + page.expectFullQueryToContain( + 'DELETE FROM `milling_loot_template` WHERE (`Entry` = 1234);\n' + + 'INSERT INTO `milling_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, ' + + '`GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 0, 0, 1, 0, 1, 0, 1, 1, \'\');' + ); + + page.setInputValueById('QuestRequired', '2'); + page.expectDiffQueryToContain( + 'DELETE FROM `milling_loot_template` WHERE (`Entry` = 1234) AND (`Item` IN (0));\n' + + 'INSERT INTO `milling_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, ' + + '`GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 0, 0, 1, 2, 1, 0, 1, 1, \'\');' + ); + page.expectFullQueryToContain( + 'DELETE FROM `milling_loot_template` WHERE (`Entry` = 1234);\n' + + 'INSERT INTO `milling_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, ' + + '`GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 0, 0, 1, 2, 1, 0, 1, 1, \'\');' + ); + + page.setInputValueById('Item', '123'); + page.expectDiffQueryToContain( + 'DELETE FROM `milling_loot_template` WHERE (`Entry` = 1234) AND (`Item` IN (123));\n' + + 'INSERT INTO `milling_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, ' + + '`GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 123, 0, 1, 2, 1, 0, 1, 1, \'\');' + ); + page.expectFullQueryToContain( + 'DELETE FROM `milling_loot_template` WHERE (`Entry` = 1234);\n' + + 'INSERT INTO `milling_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, ' + + '`GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 123, 0, 1, 2, 1, 0, 1, 1, \'\');' + ); + }); + }); + + describe('Editing existing', () => { + beforeEach(() => setup(false)); + + it('should correctly initialise', () => { + expect(page.formError.hidden).toBe(true); + page.expectDiffQueryToBeShown(); + page.expectDiffQueryToBeEmpty(); + page.expectFullQueryToContain('' + + 'DELETE FROM `milling_loot_template` WHERE (`Entry` = 1234);\n' + + 'INSERT INTO `milling_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, ' + + '`GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 0, 0, 100, 0, 1, 0, 1, 1, \'\'),\n' + + '(1234, 1, 0, 100, 0, 1, 0, 1, 1, \'\'),\n' + + '(1234, 2, 0, 100, 0, 1, 0, 1, 1, \'\');'); + expect(page.getEditorTableRowsCount()).toBe(3); + }); + + it('deleting rows should correctly work', () => { + page.deleteRow(1); + expect(page.getEditorTableRowsCount()).toBe(2); + page.expectDiffQueryToContain( + 'DELETE FROM `milling_loot_template` WHERE (`Entry` = 1234) AND (`Item` IN (1));' + ); + page.expectFullQueryToContain( + 'DELETE FROM `milling_loot_template` WHERE (`Entry` = 1234);\n' + + 'INSERT INTO `milling_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, `GroupId`, ' + + '`MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 0, 0, 100, 0, 1, 0, 1, 1, \'\'),\n' + + '(1234, 2, 0, 100, 0, 1, 0, 1, 1, \'\');' + ); + + page.deleteRow(1); + expect(page.getEditorTableRowsCount()).toBe(1); + page.expectDiffQueryToContain( + 'DELETE FROM `milling_loot_template` WHERE (`Entry` = 1234) AND (`Item` IN (1, 2));' + ); + page.expectFullQueryToContain( + 'DELETE FROM `milling_loot_template` WHERE (`Entry` = 1234);\n' + + 'INSERT INTO `milling_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, `GroupId`, ' + + '`MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 0, 0, 100, 0, 1, 0, 1, 1, \'\');' + ); + + page.deleteRow(0); + expect(page.getEditorTableRowsCount()).toBe(0); + page.expectDiffQueryToContain( + 'DELETE FROM `milling_loot_template` WHERE `Entry` = 1234;' + ); + page.expectFullQueryToBeEmpty(); + }); + + it('editing existing rows should correctly work', () => { + page.clickRowOfDatatable(1); + page.setInputValueById('LootMode', 1); + + page.clickRowOfDatatable(2); + page.setInputValueById('GroupId', 2); + + page.expectDiffQueryToContain( + 'DELETE FROM `milling_loot_template` WHERE (`Entry` = 1234) AND (`Item` IN (2));\n' + + 'INSERT INTO `milling_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, `GroupId`, ' + + '`MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 2, 0, 100, 0, 1, 2, 1, 1, \'\');' + ); + page.expectFullQueryToContain( + 'DELETE FROM `milling_loot_template` WHERE (`Entry` = 1234);\n' + + 'INSERT INTO `milling_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, `GroupId`, ' + + '`MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 0, 0, 100, 0, 1, 0, 1, 1, \'\'),\n' + + '(1234, 1, 0, 100, 0, 1, 0, 1, 1, \'\'),\n' + + '(1234, 2, 0, 100, 0, 1, 2, 1, 1, \'\');' + ); + }); + + it('combining add, edit and delete should correctly work', () => { + page.addNewRow(); + expect(page.getEditorTableRowsCount()).toBe(4); + + page.clickRowOfDatatable(1); + page.setInputValueById('Chance', 10); + expect(page.getEditorTableRowsCount()).toBe(4); + + page.deleteRow(2); + expect(page.getEditorTableRowsCount()).toBe(3); + + page.expectDiffQueryToContain( + 'DELETE FROM `milling_loot_template` WHERE (`Entry` = 1234) AND (`Item` IN (1, 2, 3));\n' + + 'INSERT INTO `milling_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, `GroupId`, ' + + '`MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 1, 0, 10, 0, 1, 0, 1, 1, \'\'),\n' + + '(1234, 3, 0, 100, 0, 1, 0, 1, 1, \'\');' + ); + page.expectFullQueryToContain( + 'DELETE FROM `milling_loot_template` WHERE (`Entry` = 1234);\n' + + 'INSERT INTO `milling_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, `GroupId`, ' + + '`MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 0, 0, 100, 0, 1, 0, 1, 1, \'\'),\n' + + '(1234, 1, 0, 10, 0, 1, 0, 1, 1, \'\'),\n' + + '(1234, 3, 0, 100, 0, 1, 0, 1, 1, \'\');' + ); + }); + + it('using the same row id for multiple rows should correctly show an error', () => { + page.clickRowOfDatatable(2); + page.setInputValueById('Item', 0); + + page.expectUniqueError(); + }); + }); + +}); diff --git a/src/app/components/editors/item/milling-loot-template/milling-loot-template.module.ts b/src/app/components/editors/item/milling-loot-template/milling-loot-template.module.ts new file mode 100644 index 00000000000..7fa74132922 --- /dev/null +++ b/src/app/components/editors/item/milling-loot-template/milling-loot-template.module.ts @@ -0,0 +1,31 @@ +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { ReactiveFormsModule } from '@angular/forms'; + +import { TopBarModule } from '../../shared/top-bar/top-bar.module'; +import { QueryOutputModule } from '../../shared/query-output/query-output.module'; +import { MillingLootTemplateComponent } from './milling-loot-template.component'; +import { ItemSelectorModule } from '../../shared/selectors/item-selector/item-selector.module'; +import { TooltipModule } from 'ngx-bootstrap'; +import { FlagsSelectorModule } from '../../shared/selectors/flags-selector/flags-selector.module'; +import { NgxDatatableModule } from '@swimlane/ngx-datatable'; + +@NgModule({ + declarations: [ + MillingLootTemplateComponent, + ], + imports: [ + BrowserModule, + ReactiveFormsModule, + TopBarModule, + QueryOutputModule, + TooltipModule.forRoot(), + ItemSelectorModule, + FlagsSelectorModule, + NgxDatatableModule, + ], + exports: [ + MillingLootTemplateComponent, + ], +}) +export class MillingLootTemplateModule {} diff --git a/src/app/components/editors/item/prospecting-loot-template/prospecting-loot-template.component.ts b/src/app/components/editors/item/prospecting-loot-template/prospecting-loot-template.component.ts new file mode 100644 index 00000000000..f29b38cb17a --- /dev/null +++ b/src/app/components/editors/item/prospecting-loot-template/prospecting-loot-template.component.ts @@ -0,0 +1,25 @@ +import { Component } from '@angular/core'; + +import { ItemHandlerService } from '../../../../services/handlers/item-handler.service'; +import { ProspectingLootTemplate } from '../../../../types/prospecting-loot-template.type'; +import { ProspectingLootTemplateService } from '../../../../services/editors/item/prospecting-loot-template.service'; +import { MultiRowEditorComponent } from '../../shared/multi-row-editor.component'; +import { LOOT_MODE } from '../../../../constants/flags/loot-mode'; + +@Component({ + selector: 'app-prospecting-loot-template', + templateUrl: '../item-loot-template/item-loot-template.component.html', + styleUrls: ['../item-loot-template/item-loot-template.component.scss'] +}) +export class ProspectingLootTemplateComponent extends MultiRowEditorComponent { + + public readonly LOOT_MODE = LOOT_MODE; + + /* istanbul ignore next */ // because of: https://github.com/gotwarlost/istanbul/issues/690 + constructor( + public editorService: ProspectingLootTemplateService, + public handlerService: ItemHandlerService, + ) { + super(editorService, handlerService); + } +} diff --git a/src/app/components/editors/item/prospecting-loot-template/prospecting-loot-template.integration.spec.ts b/src/app/components/editors/item/prospecting-loot-template/prospecting-loot-template.integration.spec.ts new file mode 100644 index 00000000000..2c8a524ee1c --- /dev/null +++ b/src/app/components/editors/item/prospecting-loot-template/prospecting-loot-template.integration.spec.ts @@ -0,0 +1,274 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { RouterTestingModule } from '@angular/router/testing'; +import { of } from 'rxjs'; +import Spy = jasmine.Spy; + +import { ProspectingLootTemplateComponent } from './prospecting-loot-template.component'; +import { ProspectingLootTemplateModule } from './prospecting-loot-template.module'; +import { QueryService } from '../../../../services/query.service'; +import { ProspectingLootTemplate } from '../../../../types/prospecting-loot-template.type'; +import { ItemHandlerService } from '../../../../services/handlers/item-handler.service'; +import { MultiRowEditorPageObject } from '../../../../test-utils/multi-row-editor-page-object'; + +class ProspectingLootTemplatePage extends MultiRowEditorPageObject {} + +describe('ProspectingLootTemplate integration tests', () => { + let component: ProspectingLootTemplateComponent; + let fixture: ComponentFixture; + let queryService: QueryService; + let querySpy: Spy; + let handlerService: ItemHandlerService; + let page: ProspectingLootTemplatePage; + + const id = 1234; + + const originalRow0 = new ProspectingLootTemplate(); + const originalRow1 = new ProspectingLootTemplate(); + const originalRow2 = new ProspectingLootTemplate(); + originalRow0.Entry = originalRow1.Entry = originalRow2.Entry = id; + originalRow0.Item = 0; + originalRow1.Item = 1; + originalRow2.Item = 2; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ + ProspectingLootTemplateModule, + RouterTestingModule, + ], + }) + .compileComponents(); + })); + + function setup(creatingNew: boolean) { + handlerService = TestBed.get(ItemHandlerService); + handlerService['_selected'] = `${id}`; + handlerService.isNew = creatingNew; + + queryService = TestBed.get(QueryService); + querySpy = spyOn(queryService, 'query').and.returnValue(of()); + + spyOn(queryService, 'selectAll').and.returnValue(of( + { results: creatingNew ? [] : [originalRow0, originalRow1, originalRow2] } + )); + + fixture = TestBed.createComponent(ProspectingLootTemplateComponent); + component = fixture.componentInstance; + page = new ProspectingLootTemplatePage(fixture); + fixture.autoDetectChanges(true); + fixture.detectChanges(); + } + + describe('Creating new', () => { + beforeEach(() => setup(true)); + + it('should correctly initialise', () => { + page.expectDiffQueryToBeEmpty(); + page.expectFullQueryToBeEmpty(); + expect(page.formError.hidden).toBe(true); + expect(page.addNewRowBtn.disabled).toBe(false); + expect(page.deleteSelectedRowBtn.disabled).toBe(true); + expect(page.getInputById('Item').disabled).toBe(true); + expect(page.getInputById('Reference').disabled).toBe(true); + expect(page.getInputById('Chance').disabled).toBe(true); + expect(page.getInputById('QuestRequired').disabled).toBe(true); + expect(page.getInputById('LootMode').disabled).toBe(true); + expect(page.getInputById('GroupId').disabled).toBe(true); + expect(page.getInputById('MinCount').disabled).toBe(true); + expect(page.getInputById('MaxCount').disabled).toBe(true); + expect(page.getInputById('Comment').disabled).toBe(true); + expect(page.getEditorTableRowsCount()).toBe(0); + }); + + it('adding new rows and executing the query should correctly work', () => { + const expectedQuery = 'DELETE FROM `prospecting_loot_template` WHERE (`Entry` = 1234) AND (`Item` IN (0, 1, 2));\n' + + 'INSERT INTO `prospecting_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, `GroupId`, ' + + '`MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 0, 0, 100, 0, 1, 0, 1, 1, \'\'),\n' + + '(1234, 1, 0, 100, 0, 1, 0, 1, 1, \'\'),\n' + + '(1234, 2, 0, 100, 0, 1, 0, 1, 1, \'\');'; + querySpy.calls.reset(); + + page.addNewRow(); + expect(page.getEditorTableRowsCount()).toBe(1); + page.addNewRow(); + expect(page.getEditorTableRowsCount()).toBe(2); + page.addNewRow(); + expect(page.getEditorTableRowsCount()).toBe(3); + page.clickExecuteQuery(); + + page.expectDiffQueryToContain(expectedQuery); + expect(querySpy).toHaveBeenCalledTimes(1); + expect(querySpy.calls.mostRecent().args[0]).toContain(expectedQuery); + }); + + it('adding a row and changing its values should correctly update the queries', () => { + page.addNewRow(); + page.expectDiffQueryToContain( + 'DELETE FROM `prospecting_loot_template` WHERE (`Entry` = 1234) AND (`Item` IN (0));\n' + + 'INSERT INTO `prospecting_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, ' + + '`GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 0, 0, 100, 0, 1, 0, 1, 1, \'\');' + ); + page.expectFullQueryToContain( + 'DELETE FROM `prospecting_loot_template` WHERE (`Entry` = 1234);\n' + + 'INSERT INTO `prospecting_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, ' + + '`GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 0, 0, 100, 0, 1, 0, 1, 1, \'\');' + ); + + page.setInputValueById('Chance', '1'); + page.expectDiffQueryToContain( + 'DELETE FROM `prospecting_loot_template` WHERE (`Entry` = 1234) AND (`Item` IN (0));\n' + + 'INSERT INTO `prospecting_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, ' + + '`GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 0, 0, 1, 0, 1, 0, 1, 1, \'\');' + ); + page.expectFullQueryToContain( + 'DELETE FROM `prospecting_loot_template` WHERE (`Entry` = 1234);\n' + + 'INSERT INTO `prospecting_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, ' + + '`GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 0, 0, 1, 0, 1, 0, 1, 1, \'\');' + ); + + page.setInputValueById('QuestRequired', '2'); + page.expectDiffQueryToContain( + 'DELETE FROM `prospecting_loot_template` WHERE (`Entry` = 1234) AND (`Item` IN (0));\n' + + 'INSERT INTO `prospecting_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, ' + + '`GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 0, 0, 1, 2, 1, 0, 1, 1, \'\');' + ); + page.expectFullQueryToContain( + 'DELETE FROM `prospecting_loot_template` WHERE (`Entry` = 1234);\n' + + 'INSERT INTO `prospecting_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, ' + + '`GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 0, 0, 1, 2, 1, 0, 1, 1, \'\');' + ); + + page.setInputValueById('Item', '123'); + page.expectDiffQueryToContain( + 'DELETE FROM `prospecting_loot_template` WHERE (`Entry` = 1234) AND (`Item` IN (123));\n' + + 'INSERT INTO `prospecting_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, ' + + '`GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 123, 0, 1, 2, 1, 0, 1, 1, \'\');' + ); + page.expectFullQueryToContain( + 'DELETE FROM `prospecting_loot_template` WHERE (`Entry` = 1234);\n' + + 'INSERT INTO `prospecting_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, ' + + '`GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 123, 0, 1, 2, 1, 0, 1, 1, \'\');' + ); + }); + }); + + describe('Editing existing', () => { + beforeEach(() => setup(false)); + + it('should correctly initialise', () => { + expect(page.formError.hidden).toBe(true); + page.expectDiffQueryToBeShown(); + page.expectDiffQueryToBeEmpty(); + page.expectFullQueryToContain('' + + 'DELETE FROM `prospecting_loot_template` WHERE (`Entry` = 1234);\n' + + 'INSERT INTO `prospecting_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, ' + + '`GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 0, 0, 100, 0, 1, 0, 1, 1, \'\'),\n' + + '(1234, 1, 0, 100, 0, 1, 0, 1, 1, \'\'),\n' + + '(1234, 2, 0, 100, 0, 1, 0, 1, 1, \'\');'); + expect(page.getEditorTableRowsCount()).toBe(3); + }); + + it('deleting rows should correctly work', () => { + page.deleteRow(1); + expect(page.getEditorTableRowsCount()).toBe(2); + page.expectDiffQueryToContain( + 'DELETE FROM `prospecting_loot_template` WHERE (`Entry` = 1234) AND (`Item` IN (1));' + ); + page.expectFullQueryToContain( + 'DELETE FROM `prospecting_loot_template` WHERE (`Entry` = 1234);\n' + + 'INSERT INTO `prospecting_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, `GroupId`, ' + + '`MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 0, 0, 100, 0, 1, 0, 1, 1, \'\'),\n' + + '(1234, 2, 0, 100, 0, 1, 0, 1, 1, \'\');' + ); + + page.deleteRow(1); + expect(page.getEditorTableRowsCount()).toBe(1); + page.expectDiffQueryToContain( + 'DELETE FROM `prospecting_loot_template` WHERE (`Entry` = 1234) AND (`Item` IN (1, 2));' + ); + page.expectFullQueryToContain( + 'DELETE FROM `prospecting_loot_template` WHERE (`Entry` = 1234);\n' + + 'INSERT INTO `prospecting_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, `GroupId`, ' + + '`MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 0, 0, 100, 0, 1, 0, 1, 1, \'\');' + ); + + page.deleteRow(0); + expect(page.getEditorTableRowsCount()).toBe(0); + page.expectDiffQueryToContain( + 'DELETE FROM `prospecting_loot_template` WHERE `Entry` = 1234;' + ); + page.expectFullQueryToBeEmpty(); + }); + + it('editing existing rows should correctly work', () => { + page.clickRowOfDatatable(1); + page.setInputValueById('LootMode', 1); + + page.clickRowOfDatatable(2); + page.setInputValueById('GroupId', 2); + + page.expectDiffQueryToContain( + 'DELETE FROM `prospecting_loot_template` WHERE (`Entry` = 1234) AND (`Item` IN (2));\n' + + 'INSERT INTO `prospecting_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, `GroupId`, ' + + '`MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 2, 0, 100, 0, 1, 2, 1, 1, \'\');' + ); + page.expectFullQueryToContain( + 'DELETE FROM `prospecting_loot_template` WHERE (`Entry` = 1234);\n' + + 'INSERT INTO `prospecting_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, `GroupId`, ' + + '`MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 0, 0, 100, 0, 1, 0, 1, 1, \'\'),\n' + + '(1234, 1, 0, 100, 0, 1, 0, 1, 1, \'\'),\n' + + '(1234, 2, 0, 100, 0, 1, 2, 1, 1, \'\');' + ); + }); + + it('combining add, edit and delete should correctly work', () => { + page.addNewRow(); + expect(page.getEditorTableRowsCount()).toBe(4); + + page.clickRowOfDatatable(1); + page.setInputValueById('Chance', 10); + expect(page.getEditorTableRowsCount()).toBe(4); + + page.deleteRow(2); + expect(page.getEditorTableRowsCount()).toBe(3); + + page.expectDiffQueryToContain( + 'DELETE FROM `prospecting_loot_template` WHERE (`Entry` = 1234) AND (`Item` IN (1, 2, 3));\n' + + 'INSERT INTO `prospecting_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, `GroupId`, ' + + '`MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 1, 0, 10, 0, 1, 0, 1, 1, \'\'),\n' + + '(1234, 3, 0, 100, 0, 1, 0, 1, 1, \'\');' + ); + page.expectFullQueryToContain( + 'DELETE FROM `prospecting_loot_template` WHERE (`Entry` = 1234);\n' + + 'INSERT INTO `prospecting_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, `GroupId`, ' + + '`MinCount`, `MaxCount`, `Comment`) VALUES\n' + + '(1234, 0, 0, 100, 0, 1, 0, 1, 1, \'\'),\n' + + '(1234, 1, 0, 10, 0, 1, 0, 1, 1, \'\'),\n' + + '(1234, 3, 0, 100, 0, 1, 0, 1, 1, \'\');' + ); + }); + + it('using the same row id for multiple rows should correctly show an error', () => { + page.clickRowOfDatatable(2); + page.setInputValueById('Item', 0); + + page.expectUniqueError(); + }); + }); + +}); diff --git a/src/app/components/editors/item/prospecting-loot-template/prospecting-loot-template.module.ts b/src/app/components/editors/item/prospecting-loot-template/prospecting-loot-template.module.ts new file mode 100644 index 00000000000..18eaf336dbd --- /dev/null +++ b/src/app/components/editors/item/prospecting-loot-template/prospecting-loot-template.module.ts @@ -0,0 +1,31 @@ +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { ReactiveFormsModule } from '@angular/forms'; + +import { TopBarModule } from '../../shared/top-bar/top-bar.module'; +import { QueryOutputModule } from '../../shared/query-output/query-output.module'; +import { ProspectingLootTemplateComponent } from './prospecting-loot-template.component'; +import { ItemSelectorModule } from '../../shared/selectors/item-selector/item-selector.module'; +import { TooltipModule } from 'ngx-bootstrap'; +import { FlagsSelectorModule } from '../../shared/selectors/flags-selector/flags-selector.module'; +import { NgxDatatableModule } from '@swimlane/ngx-datatable'; + +@NgModule({ + declarations: [ + ProspectingLootTemplateComponent, + ], + imports: [ + BrowserModule, + ReactiveFormsModule, + TopBarModule, + QueryOutputModule, + TooltipModule.forRoot(), + ItemSelectorModule, + FlagsSelectorModule, + NgxDatatableModule, + ], + exports: [ + ProspectingLootTemplateComponent, + ], +}) +export class ProspectingLootTemplateModule {} diff --git a/src/app/components/editors/item/select-item/select-item.component.html b/src/app/components/editors/item/select-item/select-item.component.html index 0cfbcd7ad59..adbc9853222 100644 --- a/src/app/components/editors/item/select-item/select-item.component.html +++ b/src/app/components/editors/item/select-item/select-item.component.html @@ -57,7 +57,13 @@ {{ row.entry }} - + + + + + + +
diff --git a/src/app/components/main-window/sidebar/sidebar.component.html b/src/app/components/main-window/sidebar/sidebar.component.html index 37f9d54b235..d103c008925 100644 --- a/src/app/components/main-window/sidebar/sidebar.component.html +++ b/src/app/components/main-window/sidebar/sidebar.component.html @@ -254,7 +254,31 @@ - +
  • + + Enchantment Template + +
  • +
  • + + Item Loot + +
  • +
  • + + Disenchant Loot + +
  • +
  • + + Prospecting Loot + +
  • +
  • + + Milling Loot + +
  • diff --git a/src/app/config/app-routing.module.ts b/src/app/config/app-routing.module.ts index ec7bd958388..43dc097f4cf 100644 --- a/src/app/config/app-routing.module.ts +++ b/src/app/config/app-routing.module.ts @@ -46,6 +46,11 @@ import { import { SelectItemComponent } from '../components/editors/item/select-item/select-item.component'; import { ItemTemplateComponent } from '../components/editors/item/item-template/item-template.component'; import { ItemHandlerService } from '../services/handlers/item-handler.service'; +import { ItemLootTemplateComponent } from '../components/editors/item/item-loot-template/item-loot-template.component'; +import { DisenchantLootTemplateComponent } from '../components/editors/item/disenchant-loot-template/disenchant-loot-template.component'; +import { ProspectingLootTemplateComponent } from '../components/editors/item/prospecting-loot-template/prospecting-loot-template.component'; +import { MillingLootTemplateComponent } from '../components/editors/item/milling-loot-template/milling-loot-template.component'; +import { ItemEnchantmentTemplateComponent } from '../components/editors/item/item-enchantment/item-enchantment-template.component'; const routes: Routes = [ { @@ -220,6 +225,31 @@ const routes: Routes = [ component: ItemTemplateComponent, canActivate: [ItemHandlerService], }, + { + path: 'item-enchantment-template', + component: ItemEnchantmentTemplateComponent, + canActivate: [ItemHandlerService], + }, + { + path: 'item-loot-template', + component: ItemLootTemplateComponent, + canActivate: [ItemHandlerService], + }, + { + path: 'disenchant-loot-template', + component: DisenchantLootTemplateComponent, + canActivate: [ItemHandlerService], + }, + { + path: 'prospecting-loot-template', + component: ProspectingLootTemplateComponent, + canActivate: [ItemHandlerService], + }, + { + path: 'milling-loot-template', + component: MillingLootTemplateComponent, + canActivate: [ItemHandlerService], + }, ] }, ]; diff --git a/src/app/constants/flags/bag-family.ts b/src/app/constants/flags/bag-family.ts new file mode 100644 index 00000000000..ddabeef9361 --- /dev/null +++ b/src/app/constants/flags/bag-family.ts @@ -0,0 +1,19 @@ +import { Flag } from '../../types/general'; + +export const BAG_FAMILY: Flag[] = [ + { bit: 0, name: 'Arrows' }, + { bit: 1, name: 'Bullets' }, + { bit: 2, name: 'Soul Shards' }, + { bit: 3, name: 'Leatherworking Supplies' }, + { bit: 4, name: 'Inscription Supplies' }, + { bit: 5, name: 'Herbs' }, + { bit: 6, name: 'Enchanting Supplies' }, + { bit: 7, name: 'Engineering Supplies' }, + { bit: 8, name: 'Keys' }, + { bit: 9, name: 'Gems' }, + { bit: 10, name: 'Mining Supplies' }, + { bit: 11, name: 'Soulbound Equipment' }, + { bit: 12, name: 'Vanity Pets' }, + { bit: 13, name: 'Currency Tokens' }, + { bit: 14, name: 'Quest Items' }, +]; diff --git a/src/app/constants/flags/item-flags-custom.ts b/src/app/constants/flags/item-flags-custom.ts new file mode 100644 index 00000000000..ee8534f0d01 --- /dev/null +++ b/src/app/constants/flags/item-flags-custom.ts @@ -0,0 +1,7 @@ +import { Flag } from '../../types/general'; + +export const ITEM_FLAGS_CUSTOM: Flag[] = [ + { bit: 0, name: 'CU_DURATION_REAL_TIME' }, + { bit: 1, name: 'CU_IGNORE_QUEST_STATUS' }, + { bit: 2, name: 'CU_FOLLOW_LOOT_RULES' }, +]; diff --git a/src/app/constants/flags/item-flags-extra.ts b/src/app/constants/flags/item-flags-extra.ts new file mode 100644 index 00000000000..d5f9e993b8c --- /dev/null +++ b/src/app/constants/flags/item-flags-extra.ts @@ -0,0 +1,8 @@ +import { Flag } from '../../types/general'; + +export const ITEM_FLAGS_EXTRA: Flag[] = [ + { bit: 0, name: 'HORDE_ONLY' }, + { bit: 1, name: 'ALLIANCE_ONLY' }, + { bit: 2, name: 'EXT_COST_REQUIRES_GOLD' }, + { bit: 4, name: 'NEED_ROLL_DISABLED' }, +]; diff --git a/src/app/constants/flags/item-flags.ts b/src/app/constants/flags/item-flags.ts new file mode 100644 index 00000000000..93af54fd6bb --- /dev/null +++ b/src/app/constants/flags/item-flags.ts @@ -0,0 +1,36 @@ +import { Flag } from '../../types/general'; + +export const ITEM_FLAGS: Flag[] = [ + { bit: 0, name: 'NO_PICKUP' }, + { bit: 1, name: 'CONJURED' }, + { bit: 2, name: 'HAS_LOOT - Item can be right clicked to open its loot' }, + { bit: 3, name: 'HEROIC_TOOLTIP - Makes green "Heroic" text appear on item' }, + { bit: 4, name: 'DEPRECATED - Cannot equip or use' }, + { bit: 5, name: 'NO_USER_DESTROY - Item can not be destroyed, except by using a spell' }, + { bit: 6, name: 'PLAYERCAST - Item\'s spells are castable by players' }, + { bit: 7, name: 'NO_EQUIP_COOLDOWN No default 30 seconds cooldown when equipped' }, + { bit: 8, name: 'MULTI_LOOT_QUEST' }, + { bit: 9, name: 'IS_WRAPPER - Item can wrap other items' }, + { bit: 10, name: 'USES_RESOURCES' }, + { bit: 11, name: 'MULTI_DROP - Looting this item does not remove it from available loot' }, + { bit: 12, name: 'ITEM_PURCHASE_RECORD - Item can be returned to the vendor for its original extended cost' }, + { bit: 13, name: 'PETITION - Item is a guild or arena charter' }, + { bit: 14, name: 'HAS_TEXT - Only readable items have this' }, + { bit: 15, name: 'NO_DISENCHANT' }, + { bit: 16, name: 'REAL_DURATION' }, + { bit: 17, name: 'NO_CREATOR' }, + { bit: 18, name: 'IS_PROSPECTABLE' }, + { bit: 19, name: 'UNIQUE_EQUIPPABLE - You can only equip one of these items' }, + { bit: 20, name: 'IGNORE_FOR_AURAS' }, + { bit: 21, name: 'IGNORE_DEFAULT_ARENA_RESTRICTIONS - Item can be used during an arena match' }, + { bit: 22, name: 'NO_DURABILITY_LOSS' }, + { bit: 23, name: 'USE_WHEN_SHAPESHIFTED - Item can be used in shapeshift forms' }, + { bit: 24, name: 'HAS_QUEST_GLOW' }, + { bit: 25, name: 'HIDE_UNUSABLE_RECIPE - Profession recipes: can only be looted if you meet requirements and don\'t already know it' }, + { bit: 26, name: 'NOT_USEABLE_IN_ARENA - Item cannot be used in arena' }, + { bit: 27, name: 'IS_BOUND_TO_ACCOUNT - Item binds to account and can be sent only to your own characters' }, + { bit: 28, name: 'NO_REAGENT_COST' }, + { bit: 29, name: 'IS_MILLABLE' }, + { bit: 30, name: 'REPORT_TO_GUILD_CHAT' }, + { bit: 31, name: 'NO_PROGRESSIVE_LOOT' }, +]; diff --git a/src/app/constants/flags/socket-color.ts b/src/app/constants/flags/socket-color.ts new file mode 100644 index 00000000000..f183ff10e00 --- /dev/null +++ b/src/app/constants/flags/socket-color.ts @@ -0,0 +1,8 @@ +import { Flag } from '../../types/general'; + +export const SOCKET_COLOR: Flag[] = [ + { bit: 1, name: 'Meta' }, + { bit: 2, name: 'Red' }, + { bit: 4, name: 'Yellow' }, + { bit: 8, name: 'Blue' }, +]; diff --git a/src/app/constants/options/creature-addon-bytes1.ts b/src/app/constants/options/creature-addon-bytes1.ts index e7de84fec5f..c53ba9110ff 100644 --- a/src/app/constants/options/creature-addon-bytes1.ts +++ b/src/app/constants/options/creature-addon-bytes1.ts @@ -3,16 +3,16 @@ import { Option } from '../../types/general'; // TODO: not all possible values are listed here export const CREATURE_ADDON_BYTES_1: Option[] = [ - { value: 0, name: 'None' }, - { value: 1 , name: 'Sitting' }, - { value: 2 , name: 'Sit chair' }, - { value: 3 , name: 'Sleep' }, - { value: 4 , name: 'Sit low chair' }, - { value: 5 , name: 'Sit medium chair' }, - { value: 6 , name: 'Sit high chair' }, - { value: 7 , name: 'Shows health bar as empty (combine with the state dead emote to make a creature look dead)' }, - { value: 8 , name: 'Makes the mob kneel' }, - { value: 9 , name: 'Submerges the creature below the ground' }, - { value: 54432 , name: 'Hover mode' }, - { value: 50331648 , name: 'Hover mode 2' }, + { value: 0, name: 'None' }, + { value: 1, name: 'Sitting' }, + { value: 2, name: 'Sit chair' }, + { value: 3, name: 'Sleep' }, + { value: 4, name: 'Sit low chair' }, + { value: 5, name: 'Sit medium chair' }, + { value: 6, name: 'Sit high chair' }, + { value: 7, name: 'Shows health bar as empty (combine with the state dead emote to make a creature look dead)' }, + { value: 8, name: 'Makes the mob kneel' }, + { value: 9, name: 'Submerges the creature below the ground' }, + { value: 54432, name: 'Hover mode' }, + { value: 50331648, name: 'Hover mode 2' }, ]; diff --git a/src/app/constants/options/creature-race.ts b/src/app/constants/options/creature-race.ts index 4e7683de83c..6f80a3985eb 100644 --- a/src/app/constants/options/creature-race.ts +++ b/src/app/constants/options/creature-race.ts @@ -1,25 +1,25 @@ import { Option } from '../../types/general'; export const CREATURE_RACE: Option[] = [ - { value: 0, name: 'HUMAN' }, - { value: 1, name: 'ORC' }, - { value: 2, name: 'DWARF' }, - { value: 3, name: 'NIGHTELF' }, - { value: 4, name: 'UNDEAD_PLAYER' }, - { value: 5, name: 'TAUREN' }, - { value: 6, name: 'GNOME' }, - { value: 7, name: 'TROLL' }, - { value: 8, name: 'GOBLIN (NOT USED)' }, - { value: 9, name: 'BLOODELF' }, - { value: 10, name: 'DRAENEI' }, - { value: 11, name: 'FEL_ORC (NOT USED)' }, - { value: 12, name: 'NAGA (NOT USED)' }, - { value: 13, name: 'BROKEN (NOT USED)' }, - { value: 14, name: 'SKELETON (NOT USED)' }, - { value: 15, name: 'VRYKUL (NOT USED)' }, - { value: 16, name: 'TUSKARR (NOT USED)' }, - { value: 17, name: 'FOREST_TROLL (NOT USED)' }, - { value: 18, name: 'TAUNKA (NOT USED)' }, - { value: 19, name: 'NORTHREND_SKELETON (NOT USED)' }, + { value: 0, name: 'HUMAN' }, + { value: 1, name: 'ORC' }, + { value: 2, name: 'DWARF' }, + { value: 3, name: 'NIGHTELF' }, + { value: 4, name: 'UNDEAD_PLAYER' }, + { value: 5, name: 'TAUREN' }, + { value: 6, name: 'GNOME' }, + { value: 7, name: 'TROLL' }, + { value: 8, name: 'GOBLIN (NOT USED)' }, + { value: 9, name: 'BLOODELF' }, + { value: 10, name: 'DRAENEI' }, + { value: 11, name: 'FEL_ORC (NOT USED)' }, + { value: 12, name: 'NAGA (NOT USED)' }, + { value: 13, name: 'BROKEN (NOT USED)' }, + { value: 14, name: 'SKELETON (NOT USED)' }, + { value: 15, name: 'VRYKUL (NOT USED)' }, + { value: 16, name: 'TUSKARR (NOT USED)' }, + { value: 17, name: 'FOREST_TROLL (NOT USED)' }, + { value: 18, name: 'TAUNKA (NOT USED)' }, + { value: 19, name: 'NORTHREND_SKELETON (NOT USED)' }, ]; diff --git a/src/app/constants/options/dmg-type.ts b/src/app/constants/options/dmg-type.ts new file mode 100644 index 00000000000..ab073e39f00 --- /dev/null +++ b/src/app/constants/options/dmg-type.ts @@ -0,0 +1,11 @@ +import { Option } from '../../types/general'; + +export const DMG_TYPE: Option[] = [ + { value: 0, name: 'Physical' }, + { value: 1, name: 'Holy' }, + { value: 2, name: 'Fire' }, + { value: 3, name: 'Nature' }, + { value: 4, name: 'Frost' }, + { value: 5, name: 'Shadow' }, + { value: 6, name: 'Arcane' }, +]; diff --git a/src/app/constants/options/foot-type.ts b/src/app/constants/options/foot-type.ts new file mode 100644 index 00000000000..a6c8794b92e --- /dev/null +++ b/src/app/constants/options/foot-type.ts @@ -0,0 +1,12 @@ +import { Option } from '../../types/general'; + +export const FOOD_TYPE: Option[] = [ + { value: 1, name: 'Meat' }, + { value: 2, name: 'Fish' }, + { value: 3, name: 'Cheese' }, + { value: 4, name: 'Bread' }, + { value: 5, name: 'Fungus' }, + { value: 6, name: 'Fruit' }, + { value: 7, name: 'Raw Meat' }, + { value: 8, name: 'Raw Fish' }, +]; diff --git a/src/app/constants/options/inventory-type.ts b/src/app/constants/options/inventory-type.ts new file mode 100644 index 00000000000..00613420daa --- /dev/null +++ b/src/app/constants/options/inventory-type.ts @@ -0,0 +1,33 @@ +import { Option } from '../../types/general'; + +export const INVENTORY_TYPE: Option[] = [ + { value: 0, name: 'NON_EQUIP' }, + { value: 1, name: 'HEAD' }, + { value: 2, name: 'NECK' }, + { value: 3, name: 'SHOULDERS' }, + { value: 4, name: 'BODY' }, + { value: 5, name: 'CHEST' }, + { value: 6, name: 'WAIST' }, + { value: 7, name: 'LEGS' }, + { value: 8, name: 'FEET' }, + { value: 9, name: 'WRISTS' }, + { value: 10, name: 'HANDS' }, + { value: 11, name: 'FINGER' }, + { value: 12, name: 'TRINKET' }, + { value: 13, name: 'WEAPON' }, + { value: 14, name: 'SHIELD' }, + { value: 15, name: 'RANGED' }, + { value: 16, name: 'CLOAK' }, + { value: 17, name: '2HWEAPON' }, + { value: 18, name: 'BAG' }, + { value: 19, name: 'TABARD' }, + { value: 20, name: 'ROBE' }, + { value: 21, name: 'WEAPONMAINHAND' }, + { value: 22, name: 'WEAPONOFFHAND' }, + { value: 23, name: 'HOLDABL' }, + { value: 24, name: 'AMMO' }, + { value: 25, name: 'THROWN' }, + { value: 26, name: 'RANGEDRIGHT' }, + { value: 27, name: 'QUIVER' }, + { value: 28, name: 'RELIC' }, +]; diff --git a/src/app/constants/options/item-bonding.ts b/src/app/constants/options/item-bonding.ts new file mode 100644 index 00000000000..d00e0c4bf05 --- /dev/null +++ b/src/app/constants/options/item-bonding.ts @@ -0,0 +1,10 @@ +import { Option } from '../../types/general'; + +export const ITEM_BONDING: Option[] = [ + { value: 0, name: 'No bounds' }, + { value: 1, name: 'Binds when picked up' }, + { value: 2, name: 'Binds when equipped' }, + { value: 3, name: 'Binds when used' }, + { value: 4, name: 'Quest item' }, + { value: 5, name: 'Quest Item1' }, +]; diff --git a/src/app/constants/options/item-class.ts b/src/app/constants/options/item-class.ts new file mode 100644 index 00000000000..0b95298faa0 --- /dev/null +++ b/src/app/constants/options/item-class.ts @@ -0,0 +1,192 @@ +import { Option } from '../../types/general'; + +export const ITEM_CLASS: Option[] = [ + { value: 0, name: 'Consumable' }, + { value: 1, name: 'Container' }, + { value: 2, name: 'Weapon' }, + { value: 3, name: 'Gem' }, + { value: 4, name: 'Armor' }, + { value: 5, name: 'Reagent' }, + { value: 6, name: 'Projectile' }, + { value: 7, name: 'Trade Goods' }, + { value: 8, name: 'Generic(OBSOLETE)' }, + { value: 9, name: 'Recipe' }, + { value: 0, name: 'Money(OBSOLETE)' }, + { value: 11, name: 'Quiver' }, + { value: 12, name: 'Quest' }, + { value: 13, name: 'Key' }, + { value: 14, name: 'Permanent(OBSOLETE)' }, + { value: 15, name: 'Miscellaneous' }, + { value: 16, name: 'Glyph' }, +]; + +export const ITEM_SUBCLASS: Option[][] = []; + +ITEM_SUBCLASS[0] = [ + { value: 0, name: 'Consumable (usability in combat depends on the assigned spell)' }, + { value: 1, name: 'Potion' }, + { value: 2, name: 'Elixir' }, + { value: 3, name: 'Flask' }, + { value: 4, name: 'Scroll' }, + { value: 5, name: 'Food & Drink' }, + { value: 6, name: 'Item Enhancement' }, + { value: 7, name: 'Bandage' }, + { value: 8, name: 'Other' }, +]; + +ITEM_SUBCLASS[1] = [ + { value: 0, name: 'Bag' }, + { value: 1, name: 'Soul Bag' }, + { value: 2, name: 'Herb Bag' }, + { value: 3, name: 'Enchanting Bag' }, + { value: 4, name: 'Engineering Bag' }, + { value: 5, name: 'Gem Bag' }, + { value: 6, name: 'Mining Bag' }, + { value: 7, name: 'Leatherworking Bag' }, + { value: 8, name: 'Inscription Bag' }, +]; + +ITEM_SUBCLASS[2] = [ + { value: 0, name: 'Axe (One-handed)' }, + { value: 1, name: 'Axe (Two-handed)' }, + { value: 2, name: 'Bow' }, + { value: 3, name: 'Gun' }, + { value: 4, name: 'Mace (One-handed)' }, + { value: 5, name: 'Mace (Two-handed)' }, + { value: 6, name: 'Polearm' }, + { value: 7, name: 'Sword (One-handed)' }, + { value: 8, name: 'Sword (Two-handed)' }, + { value: 9, name: 'Obsolete' }, + { value: 10, name: 'Staff' }, + { value: 11, name: 'Exotic' }, + { value: 12, name: 'Exotic' }, + { value: 13, name: 'Fist Weapon' }, + { value: 14, name: 'Miscellaneous (Blacksmith Hammer, Mining Pick, etc.)' }, + { value: 15, name: 'Dagger' }, + { value: 16, name: 'Thrown' }, + { value: 17, name: 'Spear' }, + { value: 18, name: 'Crossbow' }, + { value: 19, name: 'Wand' }, + { value: 20, name: 'Fishing Pole' }, +]; + +ITEM_SUBCLASS[3] = [ + { value: 0, name: 'Red' }, + { value: 1, name: 'Blue' }, + { value: 2, name: 'Yellow' }, + { value: 3, name: 'Purple' }, + { value: 4, name: 'Green' }, + { value: 5, name: 'Orange' }, + { value: 6, name: 'Meta' }, + { value: 7, name: 'Simple' }, + { value: 8, name: 'Prismatic' }, +]; + +ITEM_SUBCLASS[4] = [ + { value: 0, name: 'Miscellaneous' }, + { value: 1, name: 'Cloth' }, + { value: 2, name: 'Leather' }, + { value: 3, name: 'Mail' }, + { value: 4, name: 'Plate' }, + { value: 5, name: 'Buckler (OBSOLETE)' }, + { value: 6, name: 'Shield' }, + { value: 7, name: 'Libram' }, + { value: 8, name: 'Idol' }, + { value: 9, name: 'Totem' }, + { value: 10, name: 'Sigil' }, +]; + +ITEM_SUBCLASS[5] = [ + { value: 0, name: 'Reagent' }, +]; + +ITEM_SUBCLASS[6] = [ + { value: 0, name: 'Wand (OBSOLETE)' }, + { value: 1, name: 'Bolt (OBSOLETE)' }, + { value: 2, name: 'Arrow' }, + { value: 3, name: 'Bullet' }, + { value: 4, name: 'Thrown (OBSOLETE)' }, +]; + +ITEM_SUBCLASS[7] = [ + { value: 0, name: 'Trade Goods' }, + { value: 1, name: 'Parts' }, + { value: 2, name: 'Explosives' }, + { value: 3, name: 'Devices' }, + { value: 4, name: 'Jewelcrafting' }, + { value: 5, name: 'Cloth' }, + { value: 6, name: 'Leather' }, + { value: 7, name: 'Metal & Stone' }, + { value: 8, name: 'Meat' }, + { value: 9, name: 'Herb' }, + { value: 10, name: 'Elemental' }, + { value: 11, name: 'Other' }, + { value: 12, name: 'Enchanting' }, + { value: 13, name: 'Materials' }, + { value: 14, name: 'Armor Enchantment' }, + { value: 15, name: 'Weapon Enchantment' }, +]; + +ITEM_SUBCLASS[8] = [ + { value: 0, name: 'Generic (OBSOLETE)' }, +]; + +ITEM_SUBCLASS[9] = [ + { value: 0, name: 'Book' }, + { value: 1, name: 'Leatherworking' }, + { value: 2, name: 'Tailoring' }, + { value: 3, name: 'Engineering' }, + { value: 4, name: 'Blacksmithing' }, + { value: 5, name: 'Cooking' }, + { value: 6, name: 'Alchemy' }, + { value: 7, name: 'First Aid' }, + { value: 8, name: 'Enchanting' }, + { value: 9, name: 'Fishing' }, + { value: 10, name: 'Jewelcrafting' }, +]; + +ITEM_SUBCLASS[10] = [ + { value: 0, name: 'Money (OBSOLETE)' }, +]; + +ITEM_SUBCLASS[11] = [ + { value: 0, name: 'Quiver (OBSOLETE)' }, + { value: 1, name: 'Quiver (OBSOLETE)' }, + { value: 2, name: 'Quiver (Can hold arrows)' }, + { value: 3, name: 'Ammo Pouch (Can hold bullets)' }, +]; + +ITEM_SUBCLASS[12] = [ + { value: 0, name: 'Quest' }, +]; + +ITEM_SUBCLASS[13] = [ + { value: 0, name: 'Key' }, + { value: 1, name: 'Lockpick' }, +]; + +ITEM_SUBCLASS[14] = [ + { value: 0, name: 'Permanent' }, +]; + +ITEM_SUBCLASS[15] = [ + { value: 0, name: 'Junk' }, + { value: 1, name: 'Reagent' }, + { value: 2, name: 'Pet' }, + { value: 3, name: 'Holiday' }, + { value: 4, name: 'Other' }, + { value: 5, name: 'Mount' }, +]; + +ITEM_SUBCLASS[16] = [ + { value: 1, name: 'Warrior' }, + { value: 2, name: 'Paladin' }, + { value: 3, name: 'Hunter' }, + { value: 4, name: 'Rogue' }, + { value: 5, name: 'Priest' }, + { value: 6, name: 'Death Knight' }, + { value: 7, name: 'Shaman' }, + { value: 8, name: 'Mage' }, + { value: 9, name: 'Warlock' }, + { value: 11, name: 'Druid' }, +]; diff --git a/src/app/constants/options/item-material.ts b/src/app/constants/options/item-material.ts new file mode 100644 index 00000000000..a230dc0a36f --- /dev/null +++ b/src/app/constants/options/item-material.ts @@ -0,0 +1,14 @@ +import { Option } from '../../types/general'; + +export const ITEM_MATERIAL: Option[] = [ + { value: -1, name: 'Consumables (Food, reagents, etc...)' }, + { value: 0, name: 'Not Defined' }, + { value: 1, name: 'Metal' }, + { value: 2, name: 'Wood' }, + { value: 3, name: 'Liquid' }, + { value: 4, name: 'Jewelry' }, + { value: 5, name: 'Chain' }, + { value: 6, name: 'Plate' }, + { value: 7, name: 'Cloth' }, + { value: 8, name: 'Leather' }, +]; diff --git a/src/app/constants/options/item-quality.ts b/src/app/constants/options/item-quality.ts new file mode 100644 index 00000000000..068e8cdd65a --- /dev/null +++ b/src/app/constants/options/item-quality.ts @@ -0,0 +1,12 @@ +import { Option } from '../../types/general'; + +export const ITEM_QUALITY: Option[] = [ + { value: 0, name: 'Grey (Poor)' }, + { value: 1, name: 'White (Common)' }, + { value: 2, name: 'Green (Uncommon)' }, + { value: 3, name: 'Blue (Rare)' }, + { value: 4, name: 'Purple (Epic)' }, + { value: 5, name: 'Orange (Legendary)' }, + { value: 6, name: 'Red (Artifact)' }, + { value: 7, name: 'Gold (Bind to Account) [requires flags 134221824]' }, +]; diff --git a/src/app/constants/options/item-sheath.ts b/src/app/constants/options/item-sheath.ts new file mode 100644 index 00000000000..dd5403346de --- /dev/null +++ b/src/app/constants/options/item-sheath.ts @@ -0,0 +1,10 @@ +import { Option } from '../../types/general'; + +export const ITEM_SHEAT: Option[] = [ + { value: 1, name: `Two Handed Weapon - Diagonally across the back pointing downwards.` }, + { value: 2, name: `Staff - diagonally across the back pointing upwards.` }, + { value: 3, name: `One Handed - On the left-hand side of the character's waist.` }, + { value: 4, name: `Shield - On the middle of the character's back.` }, + { value: 5, name: `Enchanter's Rod` }, + { value: 6, name: `Off hand - On the right-hand side of the character's waist.` }, +]; diff --git a/src/app/constants/options/quest-type.ts b/src/app/constants/options/quest-type.ts index 94d7ac51326..3627b747180 100644 --- a/src/app/constants/options/quest-type.ts +++ b/src/app/constants/options/quest-type.ts @@ -1,8 +1,8 @@ import { Option } from '../../types/general'; export const QUEST_TYPE: Option[] = [ - { value: 0, name: 'The quest is enabled, but it is auto-completed when accepted; this skips quest objectives and quest details' }, - { value: 1, name: 'The quest is disabled' }, - { value: 2, name: 'The quest is enabled (does not auto-complete)' }, - { value: 3, name: 'The quest is a World Quest' }, + { value: 0, name: 'The quest is enabled, but it is auto-completed when accepted; this skips quest objectives and quest details' }, + { value: 1, name: 'The quest is disabled' }, + { value: 2, name: 'The quest is enabled (does not auto-complete)' }, + { value: 3, name: 'The quest is a World Quest' }, ]; diff --git a/src/app/constants/options/socket-bonus.ts b/src/app/constants/options/socket-bonus.ts new file mode 100644 index 00000000000..9300cc988ea --- /dev/null +++ b/src/app/constants/options/socket-bonus.ts @@ -0,0 +1,12 @@ +import { Option } from '../../types/general'; + +export const SOCKET_BONUS: Option[] = [ + { value: 3312, name: '+8 Strength'}, + { value: 3313, name: '+8 Agility'}, + { value: 3305, name: '+12 Stamina'}, + { value: 3, name: '+8 Intellect'}, + { value: 2872, name: '+9 Healing'}, + { value: 3753, name: '+9 Spell Power'}, + { value: 3877, name: '+16 Attack Power'}, + // TODO: add more options. The current ones are just some commonly used socket bonuse, not a complete list +]; diff --git a/src/app/constants/options/spell-trigger.ts b/src/app/constants/options/spell-trigger.ts new file mode 100644 index 00000000000..160890475f5 --- /dev/null +++ b/src/app/constants/options/spell-trigger.ts @@ -0,0 +1,10 @@ +import { Option } from '../../types/general'; + +export const SPELL_TRIGGER: Option[] = [ + { value: 0, name: 'Use' }, + { value: 1, name: 'On Equip' }, + { value: 2, name: 'Chance on Hit' }, + { value: 4, name: 'Soulstone' }, + { value: 5, name: 'Use with no delay' }, + { value: 6, name: 'Learn Spell ID' }, +]; diff --git a/src/app/constants/options/stat-type.ts b/src/app/constants/options/stat-type.ts new file mode 100644 index 00000000000..50dc0e0df49 --- /dev/null +++ b/src/app/constants/options/stat-type.ts @@ -0,0 +1,48 @@ +import { Option } from '../../types/general'; + +export const STAT_TYPE: Option[] = [ + { value: 0, name: 'MANA' }, + { value: 1, name: 'HEALTH' }, + { value: 3, name: 'AGILITY' }, + { value: 4, name: 'STRENGTH' }, + { value: 5, name: 'INTELLECT' }, + { value: 6, name: 'SPIRIT' }, + { value: 7, name: 'STAMINA' }, + { value: 12, name: 'DEFENSE_SKILL_RATING' }, + { value: 13, name: 'DODGE_RATING' }, + { value: 14, name: 'PARRY_RATING' }, + { value: 15, name: 'BLOCK_RATING' }, + { value: 16, name: 'HIT_MELEE_RATING' }, + { value: 17, name: 'HIT_RANGED_RATING' }, + { value: 18, name: 'HIT_SPELL_RATING' }, + { value: 19, name: 'CRIT_MELEE_RATING' }, + { value: 20, name: 'CRIT_RANGED_RATING' }, + { value: 21, name: 'CRIT_SPELL_RATING' }, + { value: 22, name: 'HIT_TAKEN_MELEE_RATING' }, + { value: 23, name: 'HIT_TAKEN_RANGED_RATING' }, + { value: 24, name: 'HIT_TAKEN_SPELL_RATING' }, + { value: 25, name: 'CRIT_TAKEN_MELEE_RATING' }, + { value: 26, name: 'CRIT_TAKEN_RANGED_RATING' }, + { value: 27, name: 'CRIT_TAKEN_SPELL_RATING' }, + { value: 28, name: 'HASTE_MELEE_RATING' }, + { value: 29, name: 'HASTE_RANGED_RATING' }, + { value: 30, name: 'HASTE_SPELL_RATING' }, + { value: 31, name: 'HIT_RATING' }, + { value: 32, name: 'CRIT_RATING' }, + { value: 33, name: 'HIT_TAKEN_RATING' }, + { value: 34, name: 'CRIT_TAKEN_RATING' }, + { value: 35, name: 'RESILIENCE_RATING' }, + { value: 36, name: 'HASTE_RATING' }, + { value: 37, name: 'EXPERTISE_RATING' }, + { value: 38, name: 'ATTACK_POWER' }, + { value: 39, name: 'RANGED_ATTACK_POWER' }, + { value: 40, name: 'FERAL_ATTACK_POWER (not used as of 3.3)' }, + { value: 41, name: 'SPELL_HEALING_DONE' }, + { value: 42, name: 'SPELL_DAMAGE_DONE' }, + { value: 43, name: 'MANA_REGENERATION' }, + { value: 44, name: 'ARMOR_PENETRATION_RATING' }, + { value: 45, name: 'SPELL_POWER' }, + { value: 46, name: 'HEALTH_REGEN' }, + { value: 47, name: 'SPELL_PENETRATION' }, + { value: 48, name: 'BLOCK_VALUE' }, +]; diff --git a/src/app/constants/options/totem-category.ts b/src/app/constants/options/totem-category.ts new file mode 100644 index 00000000000..7117e029d17 --- /dev/null +++ b/src/app/constants/options/totem-category.ts @@ -0,0 +1,37 @@ +import { Option } from '../../types/general'; + +export const TOTEM_CATEGORY: Option[] = [ + { value: 1, name: 'Skinning Knife (OLD)' }, + { value: 2, name: 'Earth Totem' }, + { value: 3, name: 'Air Totem' }, + { value: 4, name: 'Fire Totem' }, + { value: 5, name: 'Water Totem' }, + { value: 6, name: 'Runed Copper Rod' }, + { value: 7, name: 'Runed Silver Rod' }, + { value: 8, name: 'Runed Golden Rod' }, + { value: 9, name: 'Runed Truesilver Rod' }, + { value: 10, name: 'Runed Arcanite Rod' }, + { value: 11, name: 'Mining Pick (OLD)' }, + { value: 12, name: 'Philosopher\'s Stone' }, + { value: 13, name: 'Blacksmith Hammer (OLD)' }, + { value: 14, name: 'Arclight Spanner' }, + { value: 15, name: 'Gyromatic Micro-Adjustor' }, + { value: 21, name: 'Master Totem' }, + { value: 41, name: 'Runed Fel Iron Rod' }, + { value: 62, name: 'Runed Adamantite Rod' }, + { value: 63, name: 'Runed Eternium Rod' }, + { value: 81, name: 'Hollow Quill' }, + { value: 101, name: 'Runed Azurite Rod' }, + { value: 121, name: 'Virtuoso Inking Set' }, + { value: 141, name: 'Drums' }, + { value: 161, name: 'Gnomish Army Knife' }, + { value: 162, name: 'Blacksmith Hammer' }, + { value: 165, name: 'Mining Pick' }, + { value: 166, name: 'Skinning Knife' }, + { value: 167, name: 'Hammer Pick' }, + { value: 168, name: 'Bladed Pickaxe' }, + { value: 169, name: 'Flint and Tinder' }, + { value: 189, name: 'Runed Cobalt Rod' }, + { value: 190, name: 'Runed Titanium Rod' }, +]; + diff --git a/src/app/test-utils/editor-page-object.ts b/src/app/test-utils/editor-page-object.ts index 75023c1b305..23fee0fd0d6 100644 --- a/src/app/test-utils/editor-page-object.ts +++ b/src/app/test-utils/editor-page-object.ts @@ -46,8 +46,8 @@ export abstract class EditorPageObject extends PageObject { this.setInputValue(this.getInputById(inputId), value); } - getSelectorBtn(name: string) { - return this.query(`#${name}-selector-btn`); + getSelectorBtn(name: string, assert = true) { + return this.query(`#${name}-selector-btn`, assert); } getCellOfDatatableInModal(rowIndex: number, colIndex: number) {