That's typically what expect was written for:
expect -c 'spawn -noecho vi; send "iHello World!\r\33"; interact'
While expect was written for TCL in days prior to perl or python being popular, now similar modules for for perl or python are also available.
Another option is to issue TIOCSTI ioctls to your tty device to insert characters (one byte at a time) in its input queue:
perl -le 'require "sys/ioctl.ph";
ioctl(STDIN, &TIOCSTI, $_) for split "", join " ", @ARGV
' $'iHello World!\r\e'; vi
That has the benefit of avoiding an extra pseudo-terminal layer in between your terminal emulator and the application (here vi).