From 87311783ddf735e6c8b62b5bb6d2b96c7a0ef581 Mon Sep 17 00:00:00 2001 From: sunjiakun Date: Mon, 26 Jun 2023 17:48:21 +0800 Subject: [PATCH] update listxts Signed-off-by: sunjiakun --- .../src/main/ets/TestAbility/pages/Index.ets | 114 ++++++ .../src/main/ets/test/list.ets | 363 +++++++++++++++++- 2 files changed, 470 insertions(+), 7 deletions(-) diff --git a/arkui/ace_ets_components_ux/ace_ets_component_list/src/main/ets/TestAbility/pages/Index.ets b/arkui/ace_ets_components_ux/ace_ets_component_list/src/main/ets/TestAbility/pages/Index.ets index ce2cc0822..034f74b92 100644 --- a/arkui/ace_ets_components_ux/ace_ets_component_list/src/main/ets/TestAbility/pages/Index.ets +++ b/arkui/ace_ets_components_ux/ace_ets_component_list/src/main/ets/TestAbility/pages/Index.ets @@ -35,6 +35,120 @@ struct Index { Row() { Column() { List({ space: "4vp", initialIndex: 0 ,scroller: this.scrollerForList }) { + ListItemGroup(){} + .key("nurmal") + .margin({left:$r('sys.float.ohos_id_card_margin_start'),right:$r('sys.float.ohos_id_card_margin_end')}) + ListItemGroup({style: ListItemGroupStyle.CARD}) { + ListItem({style: ListItemStyle.CARD}) { + Text("ListItemGroupStyle.CARD与ListItemStyle.CARD") + .fontSize(10) + .width('100%') + .textAlign(TextAlign.Center) + } + .key("ArkUXII_List_style_0100") + + ListItem({style: ListItemStyle.NONE}) { + Text("ListItemGroupStyle.CARD与ListItemStyle.NONE") + .fontSize(10) + .width('100%') + .textAlign(TextAlign.Center) + } + .key("ArkUXII_List_style_0300") + + ListItem({style: null}) { + Text("ListItemGroupStyle.CARD与null") + .fontSize(10) + .width('100%') + .textAlign(TextAlign.Center) + } + .key("ArkUXII_List_style_0400") + + ListItem({style: undefined}) { + Text("ListItemGroupStyle.CARD与undefined") + .fontSize(10) + .width('100%') + .textAlign(TextAlign.Center) + } + .key("ArkUXII_List_style_0500") + + ListItem({style: ""}) { + Text("ListItemGroupStyle.CARD与\"\"") + .fontSize(10) + .width('100%') + .textAlign(TextAlign.Center) + } + .key("ArkUXII_List_style_0600") + + ListItem() { + Text("ListItemGroupStyle.CARD") + .fontSize(10) + .width('100%') + .textAlign(TextAlign.Center) + } + .key("ArkUXII_List_style_0700") + } + .key("ListItemGroup") + ListItemGroup({style: ListItemGroupStyle.NONE}) { + ListItem({style: ListItemStyle.CARD}) { + Text("ListItemGroupStyle.NONE与ListItemStyle.CARD") + .fontSize(10) + .width('100%') + .textAlign(TextAlign.Center) + } + } + .key("ArkUXII_List_style_0800") + + ListItemGroup({style: null}) { + ListItem({style: ListItemStyle.CARD}) { + Text("null与ListItemStyle.CARD") + .fontSize(10) + .width('100%') + .textAlign(TextAlign.Center) + } + } + .key("ArkUXII_List_style_0900") + + ListItemGroup({style: undefined}) { + ListItem({style: ListItemStyle.CARD}) { + Text("undefined与ListItemStyle.CARD") + .fontSize(10) + .width('100%') + .textAlign(TextAlign.Center) + } + } + .key("ArkUXII_List_style_1000") + + ListItemGroup({style: ""}) { + ListItem({style: ListItemStyle.CARD}) { + Text("\"\"与ListItemStyle.CARD") + .fontSize(10) + .width('100%') + .textAlign(TextAlign.Center) + } + } + .key("ArkUXII_List_style_1100") + + ListItemGroup() { + ListItem({style: ListItemStyle.CARD}) { + Text("ListItemStyle.CARD") + .fontSize(10) + .width('100%') + .textAlign(TextAlign.Center) + } + } + .key("ArkUXII_List_style_1200") + + ListItemGroup({style: "",style:ListItemGroupStyle.CARD}) { + ListItem({style: "",style: ListItemStyle.CARD}) { + Text("\"\"之后再ListItemStyle.CARD与\"\"之后再ListItemGroupStyle.CARD") + .fontSize(10) + .width('100%') + .textAlign(TextAlign.Center) + } + .key("ListItem_ArkUXII_List_style_0200") + } + .key("ArkUXII_List_style_0200") + ListItem(){ Button("scrollTo自定义弹簧曲线测试(Curves.springCurve(7, 1, 227, 33))") .key("scrollerForList") diff --git a/arkui/ace_ets_components_ux/ace_ets_component_list/src/main/ets/test/list.ets b/arkui/ace_ets_components_ux/ace_ets_component_list/src/main/ets/test/list.ets index 0a8605d5d..3389d2826 100644 --- a/arkui/ace_ets_components_ux/ace_ets_component_list/src/main/ets/test/list.ets +++ b/arkui/ace_ets_components_ux/ace_ets_component_list/src/main/ets/test/list.ets @@ -14,14 +14,362 @@ */ import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium' export default function abilityTest() { - describe('ActsAceListTest', function () { - - /** - * @tc.number ArkUX_Stage_List_RollBack_0100 - * @tc.name Testing the rolling back effect of the list and adding new parameters - * @tc.desc ScrollerForList calls the interface scrollTo(), and the animation parameter is passed in as true + /** + * @tc.number ArkUX_Stage_List_Style_0100 + * @tc.name Test the style attribute of the ListItem component and the style attribute of the + * ListItemGroup component + * @tc.desc Test setting the style parameter in ListItemGroup to ListItemGroupStyle.CARD, + * Does the style take effect after setting the style parameter in ListItem to ListItemStyle.CARD */ - it('ArkUX_Stage_List_RollBack_0100', 0, function () { + + describe('ActsAbilityTest', function () { + it('ArkUX_Stage_List_Style_0100', 0, function (done) { + setTimeout(() => { + try { + let left = JSON.stringify(JSON.parse(JSON.parse(getInspectorByKey("nurmal")) + .$attrs.margin).left) + let right = JSON.stringify(JSON.parse(JSON.parse(getInspectorByKey("nurmal")) + .$attrs.margin).right) + console.log("====> ArkUX_Stage_List_Style_0100 listItemLeft:" + left + "listItemRight:" + right) + let listItemHeight = JSON.parse(getInspectorByKey("ArkUXII_List_style_0100")) + .$attrs.size.height + console.log("====> ArkUX_Stage_List_Style_0100 listItemHeight:" + listItemHeight) + let listItemGroupLeft = JSON.stringify(JSON.parse(JSON.parse(getInspectorByKey("ListItemGroup")) + .$attrs.margin).left) + let listItemGroupRight = JSON.stringify(JSON.parse(JSON.parse(getInspectorByKey("ListItemGroup")) + .$attrs.margin).right) + console.log("====> ArkUX_Stage_List_Style_0100 listItemGroupLeft:" + listItemGroupLeft + + "listItemGroupRight:" + listItemGroupRight) + expect(listItemGroupLeft).assertEqual(left) + expect(listItemGroupRight).assertEqual(right) + expect(parseFloat(listItemHeight)).assertEqual(48); + expect(listItemHeight.substr(listItemHeight.length-2, 2)).assertEqual("vp"); + } catch (err) { + console.info('ArkUX_Stage_List_Style_0100 ERR ' + JSON.stringify(err)); + expect().assertFail(); + } + done() + }, 500); + }) + + /** + * @tc.number ArkUX_Stage_List_Style_0200 + * @tc.name Test the style attribute of the ListItem component and the style attribute of the + * ListItemGroup component + * @tc.desc Test setting the style parameter in ListItemGroup to ListItemGroupStyle.CARD, + * Does the style take effect after setting the style parameter in ListItem to ListItemStyle.CARD + */ + + it('ArkUX_Stage_List_Style_0200', 0, function (done) { + setTimeout(() => { + try { + let left = JSON.stringify(JSON.parse(JSON.parse(getInspectorByKey("nurmal")).$attrs.margin).left) + let right = JSON.stringify(JSON.parse(JSON.parse(getInspectorByKey("nurmal")).$attrs.margin).right) + console.log("====> ArkUX_Stage_List_Style_0200 listItemLeft:" + left + "listItemRight:" + right) + let listItemHeight = JSON.parse(getInspectorByKey("ListItem_ArkUXII_List_style_0200")) + .$attrs.size.height + console.log("====> ArkUX_Stage_List_Style_0200 listItemHeight:" + listItemHeight) + let listItemGroupLeft = JSON.stringify(JSON.parse(JSON.parse(getInspectorByKey("ArkUXII_List_style_0200")) + .$attrs.margin).left) + let listItemGroupRight = JSON.stringify(JSON.parse(JSON.parse(getInspectorByKey("ArkUXII_List_style_0200")) + .$attrs.margin).right) + console.log("====> ArkUX_Stage_List_Style_0200 listItemGroupLeft:" + listItemGroupLeft + + "listItemGroupRight:" + listItemGroupRight) + expect(listItemGroupLeft).assertEqual(left) + expect(listItemGroupRight).assertEqual(right) + expect(parseFloat(listItemHeight)).assertEqual(48); + expect(listItemHeight.substr(listItemHeight.length-2, 2)).assertEqual("vp"); + } catch (err) { + expect().assertFail(); + console.info('ArkUX_Stage_List_Style_0200 ERR ' + JSON.stringify(err)); + } + done() + },500); + }) + + /** + * @tc.number ArkUX_Stage_List_Style_0300 + * @tc.name Test the style attribute of the ListItem component and the style attribute of the + * ListItemGroup component + * @tc.desc Test setting the style parameter in ListItemGroup to ListItemGroupStyle.CARD, + * Does the style take effect after setting the style parameter in ListItem to ListItemStyle.NONE + */ + + it('ArkUX_Stage_List_Style_0300', 0, function (done) { + try { + let left = JSON.stringify(JSON.parse(JSON.parse(getInspectorByKey("nurmal")).$attrs.margin).left) + let right = JSON.stringify(JSON.parse(JSON.parse(getInspectorByKey("nurmal")).$attrs.margin).right) + console.log("====> ArkUX_Stage_List_Style_0300 listItemLeft:" + left + "listItemRight:" + right) + let listItemHeight = JSON.parse(getInspectorByKey("ArkUXII_List_style_0300")) + .$attrs.size.height + console.log("====> ArkUX_Stage_List_Style_0300 listItemHeight:" + listItemHeight) + let listItemGroupLeft = JSON.stringify(JSON.parse(JSON.parse(getInspectorByKey("ListItemGroup")) + .$attrs.margin).left) + let listItemGroupRight = JSON.stringify(JSON.parse(JSON.parse(getInspectorByKey("ListItemGroup")) + .$attrs.margin).right) + console.log("====> ArkUX_Stage_List_Style_0300 listItemGroupLeft:" + listItemGroupLeft + + "listItemGroupRight:" + listItemGroupRight) + expect(listItemGroupLeft).assertEqual(left) + expect(listItemGroupRight).assertEqual(right) + expect(parseFloat(listItemHeight)==48).assertFalse(); + } catch (err) { + console.info('ArkUX_Stage_List_Style_0300 ERR ' + JSON.stringify(err)); + expect().assertFail(); + } + done() + }) + + /** + * @tc.number ArkUX_Stage_List_Style_0400 + * @tc.name Test the style attribute of the ListItem component and the style attribute of the + * ListItemGroup component + * @tc.desc Test setting the style parameter in ListItemGroup to ListItemGroupStyle.CARD, + * Does the style take effect after setting the style parameter in ListItem to null + */ + + it('ArkUX_Stage_List_Style_0400', 0, function (done) { + try { + let left = JSON.stringify(JSON.parse(JSON.parse(getInspectorByKey("nurmal")).$attrs.margin).left) + let right = JSON.stringify(JSON.parse(JSON.parse(getInspectorByKey("nurmal")).$attrs.margin).right) + console.log("====> ArkUX_Stage_List_Style_0400 listItemLeft:" + left + "listItemRight:" + right) + let listItemHeight = JSON.parse(getInspectorByKey("ArkUXII_List_style_0400")) + .$attrs.size.height + console.log("====> ArkUX_Stage_List_Style_0400 listItemHeight:" + listItemHeight) + let listItemGroupLeft = JSON.stringify(JSON.parse(JSON.parse(getInspectorByKey("ListItemGroup")) + .$attrs.margin).left) + let listItemGroupRight = JSON.stringify(JSON.parse(JSON.parse(getInspectorByKey("ListItemGroup")) + .$attrs.margin).right) + console.log("====> ArkUX_Stage_List_Style_0400 listItemGroupLeft:" + listItemGroupLeft + + "listItemGroupRight:" + listItemGroupRight) + expect(listItemGroupLeft).assertEqual(left) + expect(listItemGroupRight).assertEqual(right) + expect(parseFloat(listItemHeight)==48).assertFalse(); + } catch (err) { + console.info('ArkUX_Stage_List_Style_0400 ERR ' + JSON.stringify(err)); + expect().assertFail(); + } + done() + }) + + /** + * @tc.number ArkUX_Stage_List_Style_0500 + * @tc.name Test the style attribute of the ListItem component and the style attribute of the + * ListItemGroup component + * @tc.desc Test setting the style parameter in ListItemGroup to ListItemGroupStyle.CARD, + * Does the style take effect after setting the style parameter in ListItem to undefined + */ + + it('ArkUX_Stage_List_Style_0500', 0, function (done) { + try { + let left = JSON.stringify(JSON.parse(JSON.parse(getInspectorByKey("nurmal")).$attrs.margin).left) + let right = JSON.stringify(JSON.parse(JSON.parse(getInspectorByKey("nurmal")).$attrs.margin).right) + console.log("====> ArkUX_Stage_List_Style_0500 listItemLeft:" + left + "listItemRight:" + right) + let listItemHeight = JSON.parse(getInspectorByKey("ArkUXII_List_style_0500")) + .$attrs.size.height + console.log("====> ArkUX_Stage_List_Style_0500 listItemHeight:" + listItemHeight) + let listItemGroupLeft = JSON.stringify(JSON.parse(JSON.parse(getInspectorByKey("ListItemGroup")) + .$attrs.margin).left) + let listItemGroupRight = JSON.stringify(JSON.parse(JSON.parse(getInspectorByKey("ListItemGroup")) + .$attrs.margin).right) + console.log("====> ArkUX_Stage_List_Style_0500 listItemGroupLeft:" + listItemGroupLeft + + "listItemGroupRight:" + listItemGroupRight) + expect(listItemGroupLeft).assertEqual(left) + expect(listItemGroupRight).assertEqual(right) + expect(parseFloat(listItemHeight)==48).assertFalse(); + } catch (err) { + console.info('ArkUX_Stage_List_Style_0500 ERR ' + JSON.stringify(err)); + expect().assertFail(); + } + done() + }) + + /** + * @tc.number ArkUX_Stage_List_Style_0600 + * @tc.name Test the style attribute of the ListItem component and the style attribute of the + * ListItemGroup component + * @tc.desc Test setting the style parameter in ListItemGroup to ListItemGroupStyle.CARD, + * Does the style take effect after setting the style parameter in ListItem to "" + */ + + it('ArkUX_Stage_List_Style_0600', 0, function (done) { + try { + let left = JSON.stringify(JSON.parse(JSON.parse(getInspectorByKey("nurmal")).$attrs.margin).left) + let right = JSON.stringify(JSON.parse(JSON.parse(getInspectorByKey("nurmal")).$attrs.margin).right) + console.log("====> ArkUX_Stage_List_Style_0600 listItemLeft:" + left + "listItemRight:" + right) + let listItemHeight = JSON.parse(getInspectorByKey("ArkUXII_List_style_0600")) + .$attrs.size.height + console.log("====> ArkUX_Stage_List_Style_0600 listItemHeight:" + listItemHeight) + let listItemGroupLeft = JSON.stringify(JSON.parse(JSON.parse(getInspectorByKey("ListItemGroup")) + .$attrs.margin).left) + let listItemGroupRight = JSON.stringify(JSON.parse(JSON.parse(getInspectorByKey("ListItemGroup")) + .$attrs.margin).right) + console.log("====> ArkUX_Stage_List_Style_0600 listItemGroupLeft:" + listItemGroupLeft + + "listItemGroupRight:" + listItemGroupRight) + expect(listItemGroupLeft).assertEqual(left) + expect(listItemGroupRight).assertEqual(right) + expect(parseFloat(listItemHeight)==48).assertFalse(); + } catch (err) { + console.info('ArkUX_Stage_List_Style_0600 ERR ' + JSON.stringify(err)); + expect().assertFail(); + } + done() + }) + + /** + * @tc.number ArkUX_Stage_List_Style_0700 + * @tc.name Test the style attribute of the ListItem component and the style attribute of the + * ListItemGroup component + * @tc.desc Test setting the style parameter in ListItemGroup to ListItemGroupStyle.CARD, + * Does the style take effect after setting the style parameter is not set + */ + + it('ArkUX_Stage_List_Style_0700', 0, function (done) { + try { + let left = JSON.stringify(JSON.parse(JSON.parse(getInspectorByKey("nurmal")).$attrs.margin).left) + let right = JSON.stringify(JSON.parse(JSON.parse(getInspectorByKey("nurmal")).$attrs.margin).right) + console.log("====> ArkUX_Stage_List_Style_0700 listItemLeft:" + left + "listItemRight:" + right) + let listItemHeight = JSON.parse(getInspectorByKey("ArkUXII_List_style_0700")) + .$attrs.size.height + console.log("====> ArkUX_Stage_List_Style_0700 listItemHeight:" + listItemHeight) + let listItemGroupLeft = JSON.stringify(JSON.parse(JSON.parse(getInspectorByKey("ListItemGroup")) + .$attrs.margin).left) + let listItemGroupRight = JSON.stringify(JSON.parse(JSON.parse(getInspectorByKey("ListItemGroup")) + .$attrs.margin).right) + console.log("====> ArkUX_Stage_List_Style_0700 listItemGroupLeft:" + listItemGroupLeft + + "listItemGroupRight:" + listItemGroupRight) + expect(listItemGroupLeft).assertEqual(left) + expect(listItemGroupRight).assertEqual(right) + expect(parseFloat(listItemHeight)==48).assertFalse(); + } catch (err) { + console.info('ArkUX_Stage_List_Style_0700 ERR ' + JSON.stringify(err)); + expect().assertFail(); + } + done() + }) + + /** + * @tc.number ArkUX_Stage_List_Style_0800 + * @tc.name Test the style attribute of the ListItem component and the style attribute of the + * ListItemGroup component + * @tc.desc Test setting the style parameter in ListItemGroup to ListItemGroupStyle.NONE, + * Does the style take effect after setting the style parameter in ListItem to ListItemStyle.CARD + */ + + it('ArkUX_Stage_List_Style_0800', 0, function (done) { + try { + let margin = JSON.stringify(JSON.parse(getInspectorByKey("nurmal")).$attrs.margin) + console.log("====> ArkUX_Stage_List_Style_0800 margin:" + margin) + let listItemGroupMargin = JSON.stringify(JSON.parse(getInspectorByKey("ArkUXII_List_style_0800")) + .$attrs.margin) + console.log("====> ArkUX_Stage_List_Style_0800 listItemGroupMargin:" + listItemGroupMargin) + expect(listItemGroupMargin == margin).assertFalse() + } catch (err) { + console.info('ArkUX_Stage_List_Style_0800 ERR ' + JSON.stringify(err)); + expect().assertFail(); + } + done() + }) + + /** + * @tc.number ArkUX_Stage_List_Style_0900 + * @tc.name Test the style attribute of the ListItem component and the style attribute of the + * ListItemGroup component + * @tc.desc Test setting the style parameter in ListItemGroup to null, + * Does the style take effect after setting the style parameter in ListItem to ListItemStyle.CARD + */ + + it('ArkUX_Stage_List_Style_0900', 0, function (done) { + try { + let margin = JSON.stringify(JSON.parse(getInspectorByKey("nurmal")).$attrs.margin) + console.log("====> ArkUX_Stage_List_Style_0900 margin:" + margin) + let listItemGroupMargint = JSON.stringify(JSON.parse(getInspectorByKey("ArkUXII_List_style_0900")) + .$attrs.margin) + console.log("====> ArkUX_Stage_List_Style_0900 listItemGroupMargint:" + listItemGroupMargint) + expect(listItemGroupMargint == margin).assertFalse() + } catch (err) { + console.info('ArkUX_Stage_List_Style_0900 ERR ' + JSON.stringify(err)); + expect().assertFail(); + } + done() + }) + + /** + * @tc.number ArkUX_Stage_List_Style_1000 + * @tc.name Test the style attribute of the ListItem component and the style attribute of the + * ListItemGroup component + * @tc.desc Test setting the style parameter in ListItemGroup to undefined, + * Does the style take effect after setting the style parameter in ListItem to ListItemStyle.CARD + */ + + it('ArkUX_Stage_List_Style_1000', 0, function (done) { + try { + let margin = JSON.stringify(JSON.parse(getInspectorByKey("nurmal")).$attrs.margin) + console.log("====> ArkUX_Stage_List_Style_1000 margin:" + margin) + let listItemGroupMargin = JSON.stringify(JSON.parse(getInspectorByKey("ArkUXII_List_style_1000")) + .$attrs.margin) + console.log("====> ArkUX_Stage_List_Style_1000 listItemGroupMargin:" + listItemGroupMargin) + expect(listItemGroupMargin == margin).assertFalse() + } catch (err) { + console.info('ArkUX_Stage_List_Style_1000 ERR' + JSON.stringify(err)); + expect().assertFail(); + } + done() + }) + + /** + * @tc.number ArkUX_Stage_List_Style_1100 + * @tc.name Test the style attribute of the ListItem component and the style attribute of the + * ListItemGroup component + * @tc.desc Test setting the style parameter in ListItemGroup to in "", + * Does the style take effect after setting the style parameter in ListItem to ListItemStyle.CARD + */ + + it('ArkUX_Stage_List_Style_1100', 0, function (done) { + try { + let margin = JSON.stringify(JSON.parse(getInspectorByKey("nurmal")).$attrs.margin) + console.log("====> ArkUX_Stage_List_Style_1100 margin:" + margin) + let listItemGroupMargin = JSON.stringify(JSON.parse(getInspectorByKey("ArkUXII_List_style_1100")) + .$attrs.margin) + console.log("====> ArkUX_Stage_List_Style_1100 listItemGroupMargin:" + listItemGroupMargin) + expect(listItemGroupMargin == margin).assertFalse() + } catch (err) { + console.info('ArkUX_Stage_List_Style_1100 ERR' + JSON.stringify(err)); + expect().assertFail(); + } + done() + }) + + /** + * @tc.number ArkUX_Stage_List_Style_1200 + * @tc.name Test the style attribute of the ListItem component and the style attribute of the + * ListItemGroup component + * @tc.desc Test setting the style parameter in ListItemGroup to ListItem is not set, + * Does the style take effect after setting the style parameter in ListItem to ListItemStyle.CARD + */ + + it('ArkUX_Stage_List_Style_1200', 0, function (done) { + try { + let margin = JSON.stringify(JSON.parse(getInspectorByKey("nurmal")).$attrs.margin) + console.log("====> ArkUX_Stage_List_Style_1200 margin:" + margin) + let listItemGroupMargin = JSON.stringify(JSON.parse(getInspectorByKey("ArkUXII_List_style_1200")) + .$attrs.margin) + console.log("====> ArkUX_Stage_List_Style_1200 listItemGroupMargin:" + listItemGroupMargin) + expect(listItemGroupMargin == margin).assertFalse() + } catch (err) { + console.info('ArkUX_Stage_List_Style_1200 ERR ' + JSON.stringify(err)); + expect().assertFail(); + } + done() + }) + + + /** + * @tc.number ArkUX_Stage_List_RollBack_0100 + * @tc.name Testing the rolling back effect of the list and adding new parameters + * @tc.desc ScrollerForList calls the interface scrollTo(), and the animation parameter is passed in as true + */ + + it('ArkUX_Stage_List_RollBack_0100', 0, function (done) { try { sendEventByKey("scrollerForList", 10, "") let obj = JSON.stringify(JSON.parse(getInspectorByKey("RollBack")).$attrs.scrollerForList) @@ -30,6 +378,7 @@ export default function abilityTest() { console.info('ArkUX_Stage_List_RollBack_0100 ERR ' + JSON.stringify(err)); expect().assertFail(); } + done() }) }) -- GitLab