Skip to content

Commit a6528a1

Browse files
committed
fix: Make stubbing work with minification.
This updates stubbing to use direct function references rather than name strings since the code references will be correctly updated during minification. This also makes it a bit more explicit when non-public functions are being stubbed, and it slightly simplifies stubbing logic.
1 parent a94f037 commit a6528a1

20 files changed

+50
-48
lines changed

src/screenreader/function_stubber_registry.ts

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,36 +11,34 @@
1111
// eslint-disable-next-line @typescript-eslint/no-explicit-any
1212
export type StubCallback<T> = (instance: T, ...args: any) => void;
1313

14+
/** The type representation of a generic function that can be stubbed. */
15+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
16+
export type GenericFunction = (...args: any) => any;
17+
1418
class Registration<T> {
15-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
16-
private oldMethod: ((...args: any) => any) | null = null;
19+
private oldMethod: GenericFunction | null = null;
1720

1821
constructor(
1922
readonly callback: StubCallback<T>,
20-
readonly methodNameToOverride: string,
23+
readonly methodToOverride: GenericFunction,
2124
readonly classPrototype: T,
2225
readonly ensureOneCall: boolean,
2326
) {}
2427

2528
stubPrototype(): void {
26-
// TODO: Figure out how to make this work with minification.
2729
if (this.oldMethod) {
2830
throw new Error(
29-
`Function is already stubbed: ${this.methodNameToOverride}.`,
31+
`Function is already stubbed: ${this.methodToOverride.name}.`,
3032
);
3133
}
3234
// eslint-disable-next-line @typescript-eslint/no-explicit-any
3335
const genericPrototype = this.classPrototype as any;
34-
const oldMethod = genericPrototype[this.methodNameToOverride] as (
35-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
36-
...args: any
37-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
38-
) => any;
39-
this.oldMethod = oldMethod;
36+
this.oldMethod = this.methodToOverride;
4037
// eslint-disable-next-line @typescript-eslint/no-this-alias
4138
const registration = this;
39+
const methodNameToOverride = this.methodToOverride.name;
4240
// eslint-disable-next-line @typescript-eslint/no-explicit-any
43-
genericPrototype[this.methodNameToOverride] = function (...args: any): any {
41+
genericPrototype[methodNameToOverride] = function (...args: any): any {
4442
let stubsCalled = this._internalStubsCalled as
4543
| {[key: string]: boolean}
4644
| undefined;
@@ -49,13 +47,13 @@ class Registration<T> {
4947
this._internalStubsCalled = stubsCalled;
5048
}
5149

52-
const result = oldMethod.call(this, ...args);
50+
const result = registration.methodToOverride.call(this, ...args);
5351
if (
5452
!registration.ensureOneCall ||
55-
!stubsCalled[registration.methodNameToOverride]
53+
!stubsCalled[registration.methodToOverride.name]
5654
) {
5755
registration.callback(this as unknown as T, ...args);
58-
stubsCalled[registration.methodNameToOverride] = true;
56+
stubsCalled[registration.methodToOverride.name] = true;
5957
}
6058
return result;
6159
};
@@ -96,12 +94,12 @@ export class FunctionStubber {
9694
*
9795
* @param callback The function to run when the stubbed method executes for
9896
* the first time.
99-
* @param methodNameToOverride The name of the method to override.
97+
* @param methodToOverride The method within the prototype to override.
10098
* @param classPrototype The prototype of the class being stubbed.
10199
*/
102100
registerInitializationStub<T>(
103101
callback: StubCallback<T>,
104-
methodNameToOverride: string,
102+
methodToOverride: GenericFunction,
105103
classPrototype: T,
106104
) {
107105
if (this.isFinalized) {
@@ -111,7 +109,7 @@ export class FunctionStubber {
111109
}
112110
const registration = new Registration(
113111
callback,
114-
methodNameToOverride,
112+
methodToOverride,
115113
classPrototype,
116114
true,
117115
);
@@ -127,12 +125,12 @@ export class FunctionStubber {
127125
* This will throw an error if called after stubPrototypes() has been called.
128126
*
129127
* @param callback The function to run when the stubbed method executes.
130-
* @param methodNameToOverride The name of the method to override.
128+
* @param methodToOverride The method within the prototype to override.
131129
* @param classPrototype The prototype of the class being stubbed.
132130
*/
133131
registerMethodStub<T>(
134132
callback: StubCallback<T>,
135-
methodNameToOverride: string,
133+
methodToOverride: GenericFunction,
136134
classPrototype: T,
137135
) {
138136
if (this.isFinalized) {
@@ -142,7 +140,7 @@ export class FunctionStubber {
142140
}
143141
const registration = new Registration(
144142
callback,
145-
methodNameToOverride,
143+
methodToOverride,
146144
classPrototype,
147145
false,
148146
);

src/screenreader/stuboverrides/override_block_svg.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,14 @@ FunctionStubber.getInstance().registerInitializationStub(
2222
svgPath.tabIndex = -1;
2323
blockSvgUtils.setCurrentConnectionCandidate(block, null);
2424
},
25-
'doInit_',
25+
// @ts-expect-error Access to protected property doInit_.
26+
Blockly.BlockSvg.prototype.doInit_,
2627
Blockly.BlockSvg.prototype,
2728
);
2829

2930
FunctionStubber.getInstance().registerMethodStub(
3031
(block) => blockSvgUtils.recomputeAllWorkspaceAriaTrees(block.workspace),
31-
'setParent',
32+
Blockly.BlockSvg.prototype.setParent,
3233
Blockly.BlockSvg.prototype,
3334
);
3435

@@ -39,7 +40,7 @@ FunctionStubber.getInstance().registerMethodStub(
3940
blockSvgUtils.setCurrentConnectionCandidate(block, candidate);
4041
blockSvgUtils.announceDynamicAriaStateForBlock(block, true, false);
4142
},
42-
'startDrag',
43+
Blockly.BlockSvg.prototype.startDrag,
4344
Blockly.BlockSvg.prototype,
4445
);
4546

@@ -50,7 +51,7 @@ FunctionStubber.getInstance().registerMethodStub(
5051
blockSvgUtils.setCurrentConnectionCandidate(block, candidate);
5152
blockSvgUtils.announceDynamicAriaStateForBlock(block, true, false, newLoc);
5253
},
53-
'drag',
54+
Blockly.BlockSvg.prototype.drag,
5455
Blockly.BlockSvg.prototype,
5556
);
5657

@@ -59,30 +60,30 @@ FunctionStubber.getInstance().registerMethodStub(
5960
blockSvgUtils.setCurrentConnectionCandidate(block, null);
6061
blockSvgUtils.announceDynamicAriaStateForBlock(block, false, false);
6162
},
62-
'endDrag',
63+
Blockly.BlockSvg.prototype.endDrag,
6364
Blockly.BlockSvg.prototype,
6465
);
6566

6667
FunctionStubber.getInstance().registerMethodStub(
6768
(block) => {
6869
blockSvgUtils.announceDynamicAriaStateForBlock(block, false, true);
6970
},
70-
'revertDrag',
71+
Blockly.BlockSvg.prototype.revertDrag,
7172
Blockly.BlockSvg.prototype,
7273
);
7374

7475
FunctionStubber.getInstance().registerMethodStub(
7576
(block) => {
7677
aria.setState(block.getFocusableElement(), aria.State.SELECTED, true);
7778
},
78-
'onNodeFocus',
79+
Blockly.BlockSvg.prototype.onNodeFocus,
7980
Blockly.BlockSvg.prototype,
8081
);
8182

8283
FunctionStubber.getInstance().registerMethodStub(
8384
(block) => {
8485
aria.setState(block.getFocusableElement(), aria.State.SELECTED, false);
8586
},
86-
'onNodeBlur',
87+
Blockly.BlockSvg.prototype.onNodeBlur,
8788
Blockly.BlockSvg.prototype,
8889
);

src/screenreader/stuboverrides/override_collapsible_toolbox_category.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,6 @@ FunctionStubber.getInstance().registerInitializationStub(
3030
category.getFocusableTree() as Blockly.Toolbox,
3131
);
3232
},
33-
'init',
33+
Blockly.CollapsibleToolboxCategory.prototype.init,
3434
Blockly.CollapsibleToolboxCategory.prototype,
3535
);

src/screenreader/stuboverrides/override_comment_icon.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,6 @@ FunctionStubber.getInstance().registerInitializationStub(
1717
icon.bubbleIsVisible() ? 'Close Comment' : 'Open Comment',
1818
);
1919
},
20-
'initView',
20+
Blockly.icons.CommentIcon.prototype.initView,
2121
Blockly.icons.CommentIcon.prototype,
2222
);

src/screenreader/stuboverrides/override_field.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ FunctionStubber.getInstance().registerInitializationStub(
1515
// @ts-expect-error Access to private property getTextElement.
1616
aria.setState(field.getTextElement(), aria.State.HIDDEN, true);
1717
},
18-
'createTextElement_',
18+
// @ts-expect-error Access to protected property createTextElement_.
19+
Blockly.Field.prototype.createTextElement_,
1920
Blockly.Field.prototype,
2021
);

src/screenreader/stuboverrides/override_field_checkbox.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,6 @@ FunctionStubber.getInstance().registerInitializationStub(
1818
fieldCheckbox.name ? `Checkbox ${fieldCheckbox.name}` : 'Checkbox',
1919
);
2020
},
21-
'initView',
21+
Blockly.FieldCheckbox.prototype.initView,
2222
Blockly.FieldCheckbox.prototype,
2323
);

src/screenreader/stuboverrides/override_field_dropdown.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,6 @@ FunctionStubber.getInstance().registerInitializationStub(
1818
fieldDropdown.name ? `Item ${fieldDropdown.name}` : 'Item',
1919
);
2020
},
21-
'initView',
21+
Blockly.FieldDropdown.prototype.initView,
2222
Blockly.FieldDropdown.prototype,
2323
);

src/screenreader/stuboverrides/override_field_image.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,6 @@ FunctionStubber.getInstance().registerInitializationStub(
1818
fieldImage.name ? `Image ${fieldImage.name}` : 'Image',
1919
);
2020
},
21-
'initView',
21+
Blockly.FieldImage.prototype.initView,
2222
Blockly.FieldImage.prototype,
2323
);

src/screenreader/stuboverrides/override_field_input.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,15 @@ FunctionStubber.getInstance().registerInitializationStub(
1414
(fieldNumber) => {
1515
initializeFieldInput(fieldNumber);
1616
},
17-
'init',
17+
Blockly.FieldNumber.prototype.init,
1818
Blockly.FieldNumber.prototype,
1919
);
2020

2121
FunctionStubber.getInstance().registerInitializationStub(
2222
(fieldTextInput) => {
2323
initializeFieldInput(fieldTextInput);
2424
},
25-
'init',
25+
Blockly.FieldTextInput.prototype.init,
2626
Blockly.FieldTextInput.prototype,
2727
);
2828

src/screenreader/stuboverrides/override_field_label.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,6 @@ FunctionStubber.getInstance().registerInitializationStub(
1818
fieldLabel.getText(),
1919
);
2020
},
21-
'initView',
21+
Blockly.FieldLabel.prototype.initView,
2222
Blockly.FieldLabel.prototype,
2323
);

0 commit comments

Comments
 (0)