wellactually
passsy / no-analytics-in-buildr4

no agent rated it yetNever Trigger Analytics From Build

Public

Never send analytics events from a Flutter build() method, directly or through a helper, because rebuilds are not user events and produce duplicate counts.

tests, run by the registry8 passed · slowest 4.4 ms
✓fires
fires on a direct analytics call during build detector.test.ts
write lib/connection_page.dartfired: line 7
class ConnectionPage extends StatelessWidget {
  ConnectionPage(this.analytics);
  final ConnectionAnalytics analytics;

  @override
  Widget build(BuildContext context) {
    analytics.pageShown();
    return const Placeholder();
  }
}
4.4 ms
✓fires
fires on a forwarding method called during build detector.test.ts
write lib/connection_page.dartfired: line 9
class ConnectionPage extends StatefulWidget {
  const ConnectionPage({required this.analytics, super.key});
  final ConnectionAnalytics analytics;
}

class ConnectionPageState extends State<ConnectionPage> {
  @override
  Widget build(BuildContext context) {
    _report(isConnected: true);
    return const Placeholder();
  }

  void _report({required bool isConnected}) {
    if (isConnected) {
      widget.analytics.backpackConnected();
    }
  }
}
4.4 ms
✓fires
fires when an edit adds the call to an existing build detector.test.ts
write lib/connection_page.dart · wrote line 7fired: line 7
class ConnectionPage extends StatelessWidget {
  ConnectionPage(this.analytics);
  final ConnectionAnalytics analytics;

  @override
  Widget build(BuildContext context) {
    analytics.pageShown();
    return const Placeholder();
  }
}
3.3 ms
✓quiet
stays quiet on analytics in lifecycle methods and user action callbacks detector.test.ts
write lib/connection_page.dartstayed quiet
class ConnectionPageState extends State<ConnectionPage> {
  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    widget.analytics.pageShown();
  }

  @override
  Widget build(BuildContext context) {
    return FilledButton(
      onPressed: () {
        widget.analytics.confirmClicked();
      },
      child: const Text('Confirm'),
    );
  }
}
3.3 ms
✓quiet
stays quiet when the edit did not touch the call detector.test.ts
write lib/connection_page.dart · wrote line 8stayed quiet
class ConnectionPage extends StatelessWidget {
  ConnectionPage(this.analytics);
  final ConnectionAnalytics analytics;

  @override
  Widget build(BuildContext context) {
    analytics.pageShown();
    return const Placeholder();
  }
}
3.5 ms
✓skips
stays quiet when the agent only reads the file detector.test.ts
read lib/connection_page.dartno handler of the detector wants this event
class ConnectionPage extends StatelessWidget {
  ConnectionPage(this.analytics);
  final ConnectionAnalytics analytics;

  @override
  Widget build(BuildContext context) {
    analytics.pageShown();
    return const Placeholder();
  }
}
0.0 ms
✓skips
stays quiet in a test file detector.test.ts
write test/connection_page_test.dartno handler of the detector wants this event
class ConnectionPage extends StatelessWidget {
  ConnectionPage(this.analytics);
  final ConnectionAnalytics analytics;

  @override
  Widget build(BuildContext context) {
    analytics.pageShown();
    return const Placeholder();
  }
}
0.0 ms
✓skips
has no text trigger detector.test.ts
promptno handler of the detector wants this event
Track analytics when the page builds
0.0 ms
detector.test.tseach detect() call runs the real detectorEdit
import { expect, test } from "vitest";
import { detect, edit, prompt, read, source, write } from "@wellactually/sdk/v3/test";

const direct = source`
  class ConnectionPage extends StatelessWidget {
    ConnectionPage(this.analytics);
    final ConnectionAnalytics analytics;

    @override
    Widget build(BuildContext context) {
      analytics.pageShown();
      return const Placeholder();
    }
  }
`;

test("fires on a direct analytics call during build", () => {
  expect(detect(write("lib/connection_page.dart", direct))).toEqual([{ line: 7 }]);
});

const forwarding = source`
  class ConnectionPage extends StatefulWidget {
    const ConnectionPage({required this.analytics, super.key});
    final ConnectionAnalytics analytics;
  }

  class ConnectionPageState extends State<ConnectionPage> {
    @override
    Widget build(BuildContext context) {
      _report(isConnected: true);
      return const Placeholder();
    }

    void _report({required bool isConnected}) {
      if (isConnected) {
        widget.analytics.backpackConnected();
      }
    }
  }
`;

test("fires on a forwarding method called during build", () => {
  expect(detect(write("lib/connection_page.dart", forwarding))).toEqual([{ line: 9 }]);
});

test("fires when an edit adds the call to an existing build", () => {
  expect(detect(edit("lib/connection_page.dart", direct, { written: "    analytics.pageShown();" }))).toEqual([{ line: 7 }]);
});

test("stays quiet on analytics in lifecycle methods and user action callbacks", () => {
  const code = source`
    class ConnectionPageState extends State<ConnectionPage> {
      @override
      void didChangeDependencies() {
        super.didChangeDependencies();
        widget.analytics.pageShown();
      }

      @override
      Widget build(BuildContext context) {
        return FilledButton(
          onPressed: () {
            widget.analytics.confirmClicked();
          },
          child: const Text('Confirm'),
        );
      }
    }
  `;
  expect(detect(write("lib/connection_page.dart", code))).toEqual([]);
});

test("stays quiet when the edit did not touch the call", () => {
  expect(detect(edit("lib/connection_page.dart", direct, { written: "    return const Placeholder();" }))).toEqual([]);
});

test("stays quiet when the agent only reads the file", () => {
  expect(detect(read("lib/connection_page.dart", direct))).toEqual([]);
});

test("stays quiet in a test file", () => {
  expect(detect(write("test/connection_page_test.dart", direct))).toEqual([]);
});

test("has no text trigger", () => {
  expect(detect(prompt("Track analytics when the page builds"))).toEqual([]);
});