mirror of
https://github.com/enso-org/enso.git
synced 2024-12-23 08:53:31 +03:00
6eece5f8d6
Fixes #10293 The Table Editor Widget allows adding rows and columns, editing cells and renaming columns. [Screencast from 2024-08-07 13-17-37.webm](https://github.com/user-attachments/assets/d2e708b5-6516-4107-bc17-f018e455c111) # Important Notes * The parts of Table Visualization which were useful for the widget were put in vue component. On this occasion, we use aggrid vue.
598 lines
22 KiB
TypeScript
598 lines
22 KiB
TypeScript
import test, { type Locator, type Page } from 'playwright/test'
|
|
import * as actions from './actions'
|
|
import { expect } from './customExpect'
|
|
import { mockMethodCallInfo } from './expressionUpdates'
|
|
import * as locate from './locate'
|
|
|
|
class DropDownLocator {
|
|
readonly rootWidget: Locator
|
|
readonly dropDown: Locator
|
|
readonly items: Locator
|
|
readonly selectedItems: Locator
|
|
|
|
constructor(ancestor: Locator) {
|
|
this.rootWidget = ancestor.locator('.WidgetSelection').first()
|
|
const page = ancestor.page()
|
|
// There can be only one open dropdown at a time on a page. We have to filter out the ones that
|
|
// still have leaving animation running.
|
|
this.dropDown = page.locator('.DropdownWidget:not([data-transitioning])')
|
|
this.items = this.dropDown.locator('.item')
|
|
this.selectedItems = this.dropDown.locator('.item.selected')
|
|
}
|
|
|
|
async expectVisibleWithOptions(options: string[]): Promise<void> {
|
|
await this.expectVisible()
|
|
const page = this.dropDown.page()
|
|
await expect(this.items.first()).toBeVisible()
|
|
for (const option of options) {
|
|
await expect(
|
|
this.items.filter({ has: page.getByText(option, { exact: true }) }),
|
|
).toBeVisible()
|
|
}
|
|
await expect(this.items).toHaveCount(options.length)
|
|
}
|
|
|
|
async expectVisible(): Promise<void> {
|
|
await expect(this.dropDown).toHaveCount(1)
|
|
await expect(this.dropDown).toBeVisible()
|
|
}
|
|
|
|
async expectNotVisible(): Promise<void> {
|
|
await expect(this.dropDown).toBeHidden()
|
|
}
|
|
|
|
async clickOption(option: string): Promise<void> {
|
|
const item = this.item(option)
|
|
await item.click()
|
|
}
|
|
|
|
async clickWidget(): Promise<void> {
|
|
await this.rootWidget.click()
|
|
}
|
|
|
|
selectedItem(text: string): Locator {
|
|
const page = this.dropDown.page()
|
|
return this.selectedItems.filter({ has: page.getByText(text) })
|
|
}
|
|
|
|
item(text: string): Locator {
|
|
const page = this.dropDown.page()
|
|
return this.items.filter({ has: page.getByText(text) })
|
|
}
|
|
}
|
|
|
|
test('Widget in plain AST', async ({ page }) => {
|
|
await actions.goToGraph(page)
|
|
const numberNode = locate.graphNodeByBinding(page, 'five')
|
|
const numberWidget = numberNode.locator('.WidgetNumber')
|
|
await expect(numberWidget).toBeVisible()
|
|
await expect(numberWidget).toHaveValue('5')
|
|
|
|
const listNode = locate.graphNodeByBinding(page, 'list')
|
|
const listWidget = listNode.locator('.WidgetVector')
|
|
await expect(listWidget).toBeVisible()
|
|
|
|
const textNode = locate.graphNodeByBinding(page, 'text')
|
|
const textWidget = textNode.locator('.WidgetText')
|
|
await expect(textWidget).toBeVisible()
|
|
await expect(textWidget.locator('input')).toHaveValue('test')
|
|
})
|
|
|
|
test('Multi-selection widget', async ({ page }) => {
|
|
await actions.goToGraph(page)
|
|
await mockMethodCallInfo(page, 'selected', {
|
|
methodPointer: {
|
|
module: 'Standard.Table.Table',
|
|
definedOnType: 'Standard.Table.Table.Table',
|
|
name: 'select_columns',
|
|
},
|
|
notAppliedArguments: [1],
|
|
})
|
|
|
|
// Click the argument to open the dropdown.
|
|
const node = locate.graphNodeByBinding(page, 'selected')
|
|
const topLevelArgs = node.locator('.WidgetTopLevelArgument')
|
|
await expect(topLevelArgs).toHaveCount(1)
|
|
const columnsArg = topLevelArgs.filter({ has: page.getByText('columns') })
|
|
|
|
// Get the dropdown and corresponding vector; they both have 0 items.
|
|
const dropDown = new DropDownLocator(columnsArg)
|
|
await dropDown.clickWidget()
|
|
await dropDown.expectVisibleWithOptions(['Column A', 'Column B'])
|
|
await expect(dropDown.rootWidget).toHaveClass(/multiSelect/)
|
|
const vector = node.locator('.WidgetVector')
|
|
const vectorItems = vector.locator('.item .WidgetPort input')
|
|
await expect(vector).toBeVisible()
|
|
await expect(dropDown.selectedItems).toHaveCount(0)
|
|
await expect(vectorItems).toHaveCount(0)
|
|
|
|
// Enable an item.
|
|
await dropDown.clickOption('Column A')
|
|
await expect(dropDown.selectedItem('Column A')).toExist()
|
|
await expect(vector).toBeVisible()
|
|
await expect(vectorItems).toHaveCount(1)
|
|
await expect(vectorItems.first()).toHaveValue('Column A')
|
|
await dropDown.expectVisibleWithOptions(['Column A', 'Column B'])
|
|
|
|
// Add-item button opens dropdown, after closing with escape.
|
|
await page.keyboard.press('Escape')
|
|
await dropDown.expectNotVisible()
|
|
await vector.locator('.add-item').click()
|
|
await expect(dropDown.items).toHaveCount(2)
|
|
await expect(dropDown.selectedItems).toHaveCount(1)
|
|
|
|
// Enable another item.
|
|
await dropDown.clickOption('Column B')
|
|
await expect(vectorItems).toHaveCount(2)
|
|
await expect(vectorItems.first()).toHaveValue('Column A')
|
|
await expect(vectorItems.nth(1)).toHaveValue('Column B')
|
|
await expect(dropDown.dropDown).toBeVisible()
|
|
await expect(dropDown.items).toHaveCount(2)
|
|
await expect(dropDown.selectedItems).toHaveCount(2)
|
|
|
|
// Clicking to edit an item opens the dropdown, after closing with escape.
|
|
await page.keyboard.press('Escape')
|
|
await dropDown.expectNotVisible()
|
|
await expect(vectorItems.first()).toHaveValue('Column A')
|
|
await vectorItems.first().click()
|
|
await expect(vectorItems.first()).toBeFocused()
|
|
await expect(dropDown.dropDown).toBeVisible()
|
|
|
|
// Clicking to edit a different item doesn't close the dropdown.
|
|
await expect(vectorItems.nth(1)).toHaveValue('Column B')
|
|
await vectorItems.nth(1).click()
|
|
await expect(vectorItems.nth(1)).toBeFocused()
|
|
await expect(dropDown.dropDown).toBeVisible()
|
|
|
|
// Disable an item.
|
|
await dropDown.clickOption('Column A')
|
|
await expect(vectorItems).toHaveCount(1)
|
|
await expect(vectorItems.first()).toHaveValue('Column B')
|
|
await expect(dropDown.dropDown).toBeVisible()
|
|
await expect(dropDown.items).toHaveCount(2)
|
|
await expect(dropDown.selectedItems).toHaveCount(1)
|
|
|
|
// Disable the last item.
|
|
await dropDown.clickOption('Column B')
|
|
await expect(vectorItems).toHaveCount(0)
|
|
await expect(dropDown.dropDown).toBeVisible()
|
|
await expect(dropDown.items).toHaveCount(2)
|
|
await expect(dropDown.selectedItems).toHaveCount(0)
|
|
})
|
|
|
|
test('Multi-selection widget: Item edits', async ({ page }) => {
|
|
await actions.goToGraph(page)
|
|
await mockMethodCallInfo(page, 'selected', {
|
|
methodPointer: {
|
|
module: 'Standard.Table.Table',
|
|
definedOnType: 'Standard.Table.Table.Table',
|
|
name: 'select_columns',
|
|
},
|
|
notAppliedArguments: [1],
|
|
})
|
|
|
|
// Get the dropdown and set it up by enabling two items.
|
|
const columnsArg = locate
|
|
.graphNodeByBinding(page, 'selected')
|
|
.locator('.WidgetTopLevelArgument')
|
|
.filter({ has: page.getByText('columns') })
|
|
const vectorItems = columnsArg.locator('.WidgetVector .item .WidgetPort input')
|
|
const dropDown = new DropDownLocator(columnsArg)
|
|
await dropDown.clickWidget()
|
|
await dropDown.clickOption('Column A')
|
|
await dropDown.clickOption('Column B')
|
|
|
|
// Edit an item
|
|
await expect(dropDown.selectedItem('Column A')).toExist()
|
|
await expect(dropDown.selectedItem('Column B')).toExist()
|
|
await expect(vectorItems.first()).toHaveValue('Column A')
|
|
await vectorItems.first().fill('Something Else')
|
|
await expect(dropDown.selectedItem('Column A')).toBeHidden()
|
|
await expect(dropDown.selectedItem('Column B')).toExist()
|
|
})
|
|
|
|
async function dataReadNodeWithMethodCallInfo(page: Page): Promise<Locator> {
|
|
await mockMethodCallInfo(page, 'data', {
|
|
methodPointer: {
|
|
module: 'Standard.Base.Data',
|
|
definedOnType: 'Standard.Base.Data',
|
|
name: 'read',
|
|
},
|
|
notAppliedArguments: [0, 1, 2],
|
|
})
|
|
return locate.graphNodeByBinding(page, 'data')
|
|
}
|
|
|
|
test('Selection widgets in Data.read node', async ({ page }) => {
|
|
await actions.goToGraph(page)
|
|
|
|
// Check initially visible arguments
|
|
const node = await dataReadNodeWithMethodCallInfo(page)
|
|
const topLevelArgs = node.locator('.WidgetTopLevelArgument')
|
|
await expect(topLevelArgs).toHaveCount(1)
|
|
|
|
// Check arguments after selecting node
|
|
await node.click()
|
|
await expect(topLevelArgs).toHaveCount(3)
|
|
|
|
// Set value on `on_problems` (static drop-down)
|
|
const onProblemsArg = topLevelArgs.filter({ has: page.getByText('on_problems') })
|
|
await onProblemsArg.click()
|
|
const onProblemsDropdown = new DropDownLocator(onProblemsArg)
|
|
await onProblemsDropdown.expectVisibleWithOptions(['Ignore', 'Report_Warning', 'Report_Error'])
|
|
await onProblemsDropdown.clickOption('Report_Error')
|
|
await expect(onProblemsArg.locator('.WidgetToken')).toContainText([
|
|
'Problem_Behavior',
|
|
'.',
|
|
'Report_Error',
|
|
])
|
|
|
|
// Change value on `on_problems`
|
|
await mockMethodCallInfo(page, 'data', {
|
|
methodPointer: {
|
|
module: 'Standard.Base.Data',
|
|
definedOnType: 'Standard.Base.Data',
|
|
name: 'read',
|
|
},
|
|
notAppliedArguments: [0, 1],
|
|
})
|
|
await page.getByText('Report_Error').click()
|
|
await onProblemsDropdown.expectVisibleWithOptions(['Ignore', 'Report_Warning', 'Report_Error'])
|
|
await onProblemsDropdown.clickOption('Report_Warning')
|
|
await expect(onProblemsArg.locator('.WidgetToken')).toContainText([
|
|
'Problem_Behavior',
|
|
'.',
|
|
'Report_Warning',
|
|
])
|
|
|
|
// Set value on `path` (dynamic config)
|
|
const pathArg = topLevelArgs.filter({ has: page.getByText('path') })
|
|
await pathArg.click()
|
|
const pathDropdown = new DropDownLocator(pathArg)
|
|
await pathDropdown.expectVisibleWithOptions(['Choose file…', 'File 1', 'File 2'])
|
|
await pathDropdown.clickOption('File 2')
|
|
await expect(pathArg.locator('.WidgetText > input')).toHaveValue('File 2')
|
|
|
|
// Change value on `path` (dynamic config)
|
|
await mockMethodCallInfo(page, 'data', {
|
|
methodPointer: {
|
|
module: 'Standard.Base.Data',
|
|
definedOnType: 'Standard.Base.Data',
|
|
name: 'read',
|
|
},
|
|
notAppliedArguments: [1],
|
|
})
|
|
await page.getByText('path').click()
|
|
await pathDropdown.expectVisibleWithOptions(['Choose file…', 'File 1', 'File 2'])
|
|
await pathDropdown.clickOption('File 1')
|
|
await expect(pathArg.locator('.WidgetText > input')).toHaveValue('File 1')
|
|
})
|
|
|
|
test('Selection widget with text widget as input', async ({ page }) => {
|
|
await actions.goToGraph(page)
|
|
|
|
const node = await dataReadNodeWithMethodCallInfo(page)
|
|
const topLevelArgs = node.locator('.WidgetTopLevelArgument')
|
|
const pathArg = topLevelArgs.filter({ has: page.getByText('path') })
|
|
const pathDropdown = new DropDownLocator(pathArg)
|
|
const pathArgInput = pathArg.locator('.WidgetText > input')
|
|
await pathArg.click()
|
|
await pathDropdown.expectVisible()
|
|
await pathDropdown.clickOption('File 2')
|
|
await expect(pathArgInput).toHaveValue('File 2')
|
|
|
|
// Editing text input shows and filters drop down
|
|
await pathArgInput.click()
|
|
await pathDropdown.expectVisibleWithOptions(['Choose file…', 'File 1', 'File 2'])
|
|
await page.keyboard.insertText('File 1')
|
|
await pathDropdown.expectVisibleWithOptions(['File 1'])
|
|
// Clearing input should show all text literal options
|
|
await pathArgInput.clear()
|
|
await pathDropdown.expectVisibleWithOptions(['File 1', 'File 2'])
|
|
|
|
// Esc should cancel editing and close drop down
|
|
await page.keyboard.press('Escape')
|
|
await expect(pathArgInput).not.toBeFocused()
|
|
await expect(pathArgInput).toHaveValue('File 2')
|
|
await expect(pathDropdown.dropDown).not.toBeVisible()
|
|
|
|
// Choosing entry should finish editing
|
|
await pathArgInput.click()
|
|
await pathDropdown.expectVisibleWithOptions(['Choose file…', 'File 1', 'File 2'])
|
|
await page.keyboard.insertText('File')
|
|
await pathDropdown.expectVisibleWithOptions(['File 1', 'File 2'])
|
|
await pathDropdown.clickOption('File 1')
|
|
await expect(pathArgInput).not.toBeFocused()
|
|
await expect(pathArgInput).toHaveValue('File 1')
|
|
await expect(pathDropdown.dropDown).not.toBeVisible()
|
|
|
|
// Clicking-off and pressing enter should accept text as-is
|
|
await pathArgInput.click()
|
|
await pathDropdown.expectVisibleWithOptions(['Choose file…', 'File 1', 'File 2'])
|
|
await page.keyboard.insertText('File')
|
|
await page.keyboard.press('Enter')
|
|
await expect(pathArgInput).not.toBeFocused()
|
|
await expect(pathArgInput).toHaveValue('File')
|
|
await expect(pathDropdown.dropDown).not.toBeVisible()
|
|
|
|
await pathArgInput.click()
|
|
await pathDropdown.expectVisibleWithOptions(['Choose file…', 'File 1', 'File 2'])
|
|
await page.keyboard.insertText('Foo')
|
|
await expect(pathArgInput).toHaveValue('Foo')
|
|
await page.mouse.click(200, 200)
|
|
await expect(pathArgInput).not.toBeFocused()
|
|
await expect(pathArgInput).toHaveValue('Foo')
|
|
await expect(pathDropdown.dropDown).not.toBeVisible()
|
|
})
|
|
|
|
test('File Browser widget', async ({ page }) => {
|
|
await actions.goToGraph(page)
|
|
await mockMethodCallInfo(page, 'data', {
|
|
methodPointer: {
|
|
module: 'Standard.Base.Data',
|
|
definedOnType: 'Standard.Base.Data',
|
|
name: 'read',
|
|
},
|
|
notAppliedArguments: [0, 1, 2],
|
|
})
|
|
// Wait for arguments to load.
|
|
const node = locate.graphNodeByBinding(page, 'data')
|
|
const topLevelArgs = node.locator('.WidgetTopLevelArgument')
|
|
await expect(topLevelArgs).toHaveCount(1)
|
|
const pathArg = topLevelArgs.filter({ has: page.getByText('path') })
|
|
const pathDropdown = new DropDownLocator(pathArg)
|
|
await pathArg.click()
|
|
await pathDropdown.expectVisibleWithOptions(['Choose file…', 'File 1', 'File 2'])
|
|
await pathDropdown.clickOption('Choose file…')
|
|
await expect(pathArg.locator('.WidgetText > input')).toHaveValue('/path/to/some/mock/file')
|
|
})
|
|
|
|
test('Managing aggregates in `aggregate` node', async ({ page }) => {
|
|
await actions.goToGraph(page)
|
|
await mockMethodCallInfo(page, 'aggregated', {
|
|
methodPointer: {
|
|
module: 'Standard.Table.Table',
|
|
definedOnType: 'Standard.Table.Table.Table',
|
|
name: 'aggregate',
|
|
},
|
|
notAppliedArguments: [1, 2, 3],
|
|
})
|
|
|
|
// Check initially visible arguments
|
|
const node = locate.graphNodeByBinding(page, 'aggregated')
|
|
const topLevelArgs = node.locator('.WidgetTopLevelArgument')
|
|
await expect(topLevelArgs).toHaveCount(1)
|
|
|
|
// Check arguments after selecting node
|
|
await node.click()
|
|
await expect(topLevelArgs).toHaveCount(3)
|
|
|
|
// Add first aggregate
|
|
const columnsArg = topLevelArgs.filter({ has: page.getByText('columns') })
|
|
|
|
await columnsArg.locator('.add-item').click()
|
|
await expect(columnsArg.locator('.WidgetToken')).toContainText([
|
|
'Aggregate_Column',
|
|
'.',
|
|
'Group_By',
|
|
])
|
|
await mockMethodCallInfo(
|
|
page,
|
|
{
|
|
binding: 'aggregated',
|
|
expr: 'Aggregate_Column.Group_By',
|
|
},
|
|
{
|
|
methodPointer: {
|
|
module: 'Standard.Table.Aggregate_Column',
|
|
definedOnType: 'Standard.Table.Aggregate_Column.Aggregate_Column',
|
|
name: 'Group_By',
|
|
},
|
|
notAppliedArguments: [0, 1],
|
|
},
|
|
)
|
|
|
|
// Change aggregation type
|
|
const columnsDropdown = new DropDownLocator(columnsArg)
|
|
await columnsDropdown.clickWidget()
|
|
await columnsDropdown.expectVisibleWithOptions(['Group_By', 'Count', 'Count_Distinct'])
|
|
await columnsDropdown.clickOption('Count_Distinct')
|
|
await expect(columnsArg.locator('.WidgetToken')).toContainText([
|
|
'Aggregate_Column',
|
|
'.',
|
|
'Count_Distinct',
|
|
])
|
|
await mockMethodCallInfo(
|
|
page,
|
|
{
|
|
binding: 'aggregated',
|
|
expr: 'Aggregate_Column.Count_Distinct',
|
|
},
|
|
{
|
|
methodPointer: {
|
|
module: 'Standard.Table.Aggregate_Column',
|
|
definedOnType: 'Standard.Table.Aggregate_Column.Aggregate_Column',
|
|
name: 'Count_Distinct',
|
|
},
|
|
notAppliedArguments: [0, 1, 2],
|
|
},
|
|
)
|
|
|
|
// Set column
|
|
const firstItem = columnsArg.locator('.item > .WidgetPort > .WidgetSelection').nth(0)
|
|
const firstItemDropdown = new DropDownLocator(firstItem)
|
|
await firstItemDropdown.clickWidget()
|
|
await firstItemDropdown.expectVisibleWithOptions(['column 1', 'column 2'])
|
|
await firstItemDropdown.clickOption('column 1')
|
|
await expect(columnsArg.locator('.WidgetToken')).toContainText([
|
|
'Aggregate_Column',
|
|
'.',
|
|
'Count_Distinct',
|
|
])
|
|
await expect(columnsArg.locator('.WidgetText > input').first()).toHaveValue('column 1')
|
|
|
|
// Add another aggregate
|
|
await columnsArg.locator('.add-item').click()
|
|
await expect(columnsArg.locator('.WidgetToken')).toContainText([
|
|
'Aggregate_Column',
|
|
'.',
|
|
'Count_Distinct',
|
|
'Aggregate_Column',
|
|
'.',
|
|
'Group_By',
|
|
])
|
|
await mockMethodCallInfo(
|
|
page,
|
|
{
|
|
binding: 'aggregated',
|
|
expr: 'Aggregate_Column.Group_By',
|
|
},
|
|
{
|
|
methodPointer: {
|
|
module: 'Standard.Table.Aggregate_Column',
|
|
definedOnType: 'Standard.Table.Aggregate_Column.Aggregate_Column',
|
|
name: 'Group_By',
|
|
},
|
|
notAppliedArguments: [0, 1],
|
|
},
|
|
)
|
|
|
|
// Set new aggregate's column
|
|
const secondItem = columnsArg.locator('.item > .WidgetPort > .WidgetSelection').nth(1)
|
|
const secondItemDropdown = new DropDownLocator(secondItem)
|
|
await secondItemDropdown.clickWidget()
|
|
await secondItemDropdown.expectVisibleWithOptions(['column 1', 'column 2'])
|
|
await secondItemDropdown.clickOption('column 2')
|
|
await expect(secondItem.locator('.WidgetToken')).toContainText([
|
|
'Aggregate_Column',
|
|
'.',
|
|
'Group_By',
|
|
])
|
|
await expect(secondItem.locator('.WidgetText > input').first()).toHaveValue('column 2')
|
|
|
|
// Switch aggregates
|
|
//TODO[ao] I have no idea how to emulate drag. Simple dragTo does not work (some element seem to capture event).
|
|
// When hovered, the handle becomes available after some time, but still mouse events don't have any effect.
|
|
// I have no time now to investigate this.
|
|
// Once fixed, add also removing element from vector here.
|
|
|
|
// await columnsArg.locator('.item > .handle').nth(1).hover({ force: true })
|
|
// await columnsArg.locator('.item > .handle').nth(1).hover()
|
|
// await page.mouse.down()
|
|
// await columnsArg.locator('.item > .handle').nth(0).hover({ force: true })
|
|
// await columnsArg.locator('.item > .handle').nth(0).hover()
|
|
// await page.mouse.up()
|
|
// await expect(columnsArg.locator('.WidgetToken')).toContainText([
|
|
// 'Aggregate_Column',
|
|
// '.',
|
|
// 'Group_By',
|
|
// '"',
|
|
// 'column 2',
|
|
// '"',
|
|
// 'Aggregate_Column',
|
|
// '.',
|
|
// 'Count_Distinct',
|
|
// '"',
|
|
// 'column 1',
|
|
// '"',
|
|
// ])
|
|
})
|
|
|
|
// Test that autoscoped constructors provide argument placeholders.
|
|
// This test can be removed when `aggregate` inserts autoscoped constructors by default,
|
|
// so this behavior will be tested in regular `aggregate` tests.
|
|
test('Autoscoped constructors', async ({ page }) => {
|
|
await actions.goToGraph(page)
|
|
await mockMethodCallInfo(page, 'autoscoped', {
|
|
methodPointer: {
|
|
module: 'Standard.Table.Table',
|
|
definedOnType: 'Standard.Table.Table.Table',
|
|
name: 'aggregate',
|
|
},
|
|
notAppliedArguments: [2, 3],
|
|
})
|
|
await mockMethodCallInfo(
|
|
page,
|
|
{ binding: 'autoscoped', expr: '..Group_By' },
|
|
{
|
|
methodPointer: {
|
|
module: 'Standard.Table.Aggregate_Column',
|
|
definedOnType: 'Standard.Table.Aggregate_Column.Aggregate_Column',
|
|
name: 'Group_By',
|
|
},
|
|
notAppliedArguments: [0, 1],
|
|
},
|
|
)
|
|
const node = locate.graphNodeByBinding(page, 'autoscoped')
|
|
const topLevelArgs = node.locator('.WidgetTopLevelArgument')
|
|
// Wait for hidden arguments to appear after selecting the node.
|
|
await node.click()
|
|
await expect(topLevelArgs).toHaveCount(3)
|
|
|
|
const groupBy = node.locator('.item').nth(0)
|
|
await expect(groupBy).toBeVisible()
|
|
await expect(groupBy.locator('.WidgetArgumentName')).toContainText(['column', 'new_name'])
|
|
})
|
|
|
|
test('Table widget', async ({ page }) => {
|
|
await actions.goToGraph(page)
|
|
|
|
// Adding `Table.new` component will display the widget
|
|
await locate.addNewNodeButton(page).click()
|
|
await expect(locate.componentBrowser(page)).toBeVisible()
|
|
await page.keyboard.type('Table.new')
|
|
// Wait for CB entry to appear; this way we're sure about node name (binding).
|
|
await expect(locate.componentBrowserSelectedEntry(page)).toHaveCount(1)
|
|
await expect(locate.componentBrowserSelectedEntry(page)).toHaveText('Table.new')
|
|
await page.keyboard.press('Enter')
|
|
const node = locate.selectedNodes(page)
|
|
await expect(node).toHaveCount(1)
|
|
await expect(node).toBeVisible()
|
|
await mockMethodCallInfo(
|
|
page,
|
|
{ binding: 'table1', expr: 'Table.new' },
|
|
{
|
|
methodPointer: {
|
|
module: 'Standard.Table.Table',
|
|
definedOnType: 'Standard.Table.Table.Table',
|
|
name: 'new',
|
|
},
|
|
notAppliedArguments: [0],
|
|
},
|
|
)
|
|
const widget = node.locator('.WidgetTableEditor')
|
|
await expect(widget).toBeVisible()
|
|
await expect(widget.locator('.ag-header-cell-text')).toHaveText('New Column')
|
|
await expect(widget.locator('.ag-header-cell-text')).toHaveClass(/(?<=^| )virtualColumn(?=$| )/)
|
|
// There's one empty cell, allowing creating first row and column
|
|
await expect(widget.locator('.ag-cell')).toHaveCount(1)
|
|
|
|
// Putting first value
|
|
await widget.locator('.ag-cell').dblclick()
|
|
await page.keyboard.type('Value')
|
|
await page.keyboard.press('Enter')
|
|
// There will be new blank column and new blank row allowing adding new columns and rows
|
|
// (so 4 cells in total)
|
|
await expect(widget.locator('.ag-header-cell-text')).toHaveText(['New Column', 'New Column'])
|
|
await expect(widget.locator('.ag-cell')).toHaveText(['Value', '', '', ''])
|
|
|
|
// Renaming column
|
|
await widget.locator('.ag-header-cell-text').first().dblclick()
|
|
await page.keyboard.type('Header')
|
|
await page.keyboard.press('Enter')
|
|
await expect(widget.locator('.ag-header-cell-text')).toHaveText(['Header', 'New Column'])
|
|
|
|
// Switching edit between cells and headers - check we will never edit two things at once.
|
|
await expect(widget.locator('.ag-text-field-input')).toHaveCount(0)
|
|
await widget.locator('.ag-header-cell-text').first().dblclick()
|
|
await expect(widget.locator('.ag-text-field-input')).toHaveCount(1)
|
|
await widget.locator('.ag-cell').first().dblclick()
|
|
await expect(widget.locator('.ag-text-field-input')).toHaveCount(1)
|
|
await widget.locator('.ag-header-cell-text').first().dblclick()
|
|
await expect(widget.locator('.ag-text-field-input')).toHaveCount(1)
|
|
await widget.locator('.ag-header-cell-text').last().dblclick()
|
|
await expect(widget.locator('.ag-text-field-input')).toHaveCount(1)
|
|
await page.keyboard.press('Escape')
|
|
await expect(widget.locator('.ag-text-field-input')).toHaveCount(0)
|
|
})
|